diff --git a/README.md b/README.md index 15cf2f1b72..59eab3cff9 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,47 @@ _The number of blocks that the `hypersdk` stores on-disk, the `AcceptedBlockWind to an arbitrary depth (or set to `MaxInt` to keep all blocks). To limit disk IO used to serve blocks over the P2P network, `hypervms` can configure `AcceptedBlockWindowCache` to store recent blocks in memory._ +### WASM-Based Programs +In the `hypersdk`, [smart contracts](https://ethereum.org/en/developers/docs/smart-contracts/) +(e.g. programs that run on blockchains) are referred to simply as `programs`. `Programs` +are [WASM-based](https://webassembly.org/) binaries that can be invoked during block +execution to perform arbitrary state transitions. This is a more flexible, yet less performant, +alternative to defining all `Auth` and/or `Actions` that can be invoked in the `hypervm` in the +`hypervm's` code (like the `tokenvm`). + +Because the `hypersdk` can execute arbitrary WASM, any language (Rust, C, C++, Zig, etc.) that can +be compiled to WASM can be used to write `programs`. You can view a collection of +Rust-based `programs` [here](https://github.com/ava-labs/hypersdk/tree/main/x/programs/rust/examples). + +### Account Abstraction +The `hypersdk` provides out-of-the-box support for arbitrary transaction authorization logic. +Each `hypersdk` transaction includes an `Auth` object that implements an +`Actor` function (identity that participates in an `Action`) and a `Sponsor` function (identity +that pays fees). These two identities could be the same (if using a simple signature +verification `Auth` module) but may be different (if using a "gas relayer" `Auth` module). + +`Auth` modules may be hardcoded, like in +[`morpheusvm`](https://github.com/ava-labs/hypersdk/tree/main/examples/morpheusvm/auth) and +[`tokenvm`](https://github.com/ava-labs/hypersdk/tree/main/examples/tokenvm/auth), or execute +a `program` (i.e. a custom deployed multi-sig). To allow for easy interaction between different +`Auth` modules (and to ensure `Auth` modules can't interfere with each other), the +`hypersdk` employs a standard, 33-byte addressing scheme: ``. Transaction +verification ensures that any `Actor` and `Sponsor` returned by an `Auth` module +must have the same `` as the module generating an address. The 32-byte hash (``) +is used to uniquely identify accounts within an `Auth` scheme. For `programs`, this +will likely be the `txID` when the `program` was deployed and will be the hash +of the public key for pure cryptographic primitives (the indirect benefit of this +is that account public keys are obfuscated until used). + +_Because transaction IDs are used to prevent replay, it is critical that any signatures used +in `Auth` are [not malleable](https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki). +If malleable signatures are used, it would be trivial for an attacker to generate additional, valid +transactions from an existing transaction and submit it to the network (duplicating whatever `Action` was +specified by the sender)._ + +_It is up to each `Auth` module to limit the computational complexity of `Auth.AsyncVerify()` +and `Auth.Verify()` to prevent a DoS (invalid `Auth` will not charge `Auth.Sponsor()`)._ + ### Optimized Block Execution Out-of-the-Box The `hypersdk` is primarily about an obsession with hyper-speed and hyper-scalability (and making it easy for developers to achieve both by @@ -192,18 +233,6 @@ capability for any `Auth` module that implements the `AuthBatchVerifier` interfa even parallelizing batch computation for systems that only use a single-thread to verify a batch. -### WASM-Based Programs -In the `hypersdk`, [smart contracts](https://ethereum.org/en/developers/docs/smart-contracts/) -(e.g. programs that run on blockchains) are referred to simply as `programs`. `Programs` -are [WASM-based](https://webassembly.org/) binaries that can be invoked during block -execution to perform arbitrary state transitions. This is a more flexible, yet less performant, -alternative to defining all `Auth` and/or `Actions` that can be invoked in the `hypervm` in the -`hypervm's` code (like the `tokenvm`). - -Because the `hypersdk` can execute arbitrary WASM, any language (Rust, C, C++, Zig, etc.) that can -be compiled to WASM can be used to write `programs`. You can view a collection of -Rust-based `programs` [here](https://github.com/ava-labs/hypersdk/tree/main/x/programs/rust/examples). - ### Multidimensional Fee Pricing Instead of mapping transaction resource usage to a one-dimensional unit (i.e. "gas" or "fuel"), the `hypersdk` utilizes five independently parameterized unit dimensions @@ -371,14 +400,6 @@ is executed which modifies the same value, the net cost for modifying the key to the `hypervm` (and to the entire network) is much cheaper than modifying a new key. -### Account Abstraction -The `hypersdk` makes no assumptions about how `Actions` (the primitive for -interactions with any `hyperchain`, as explained below) are verified. Rather, -`hypervms` provide the `hypersdk` with a registry of supported `Auth` modules -that can be used to validate each type of transaction. These `Auth` modules can -perform simple things like signature verification or complex tasks like -executing a WASM blob. - ### Nonce-less and Expiring Transactions `hypersdk` transactions don't use [nonces](https://help.myetherwallet.com/en/articles/5461509-what-is-a-nonce) to protect against replay attack like many other account-based blockchains. This means users @@ -397,12 +418,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). -_Because transaction IDs are used to prevent replay, it is critical that any signatures used -in `Auth` are [not malleable](https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki). -If malleable signatures are used, it would be trivial for an attacker to generate additional, valid -transactions from an existing transaction and submit it to the network (duplicating whatever `Action` was -specified by the sender)._ - ### 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 @@ -803,9 +818,17 @@ type Auth interface { action Action, ) (computeUnits uint64, err error) - // Payer is the owner of [Auth]. It is used by the mempool to ensure that there aren't too many transactions - // from a single Payer. - Payer() []byte + // Actor is the subject of [Action]. + // + // To avoid collisions with other [Auth] modules, this must be prefixed + // by the [TypeID]. + Actor() codec.Address + + // Sponsor is the fee payer of [Auth]. + // + // To avoid collisions with other [Auth] modules, this must be prefixed + // by the [TypeID]. + Sponsor() codec.Address // CanDeduct returns an error if [amount] cannot be paid by [Auth]. CanDeduct(ctx context.Context, im state.Immutable, amount uint64) error diff --git a/chain/dependencies.go b/chain/dependencies.go index 4175b761d1..ad5b0a039e 100644 --- a/chain/dependencies.go +++ b/chain/dependencies.go @@ -286,9 +286,17 @@ type Auth interface { action Action, ) (computeUnits uint64, err error) - // Payer is the owner of [Auth]. It is used by the mempool to ensure that there aren't too many transactions - // from a single Payer. - Payer() []byte + // Actor is the subject of [Action]. + // + // To avoid collisions with other [Auth] modules, this must be prefixed + // by the [TypeID]. + Actor() codec.Address + + // Sponsor is the fee payer of [Auth]. + // + // To avoid collisions with other [Auth] modules, this must be prefixed + // by the [TypeID]. + Sponsor() codec.Address // CanDeduct returns an error if [amount] cannot be paid by [Auth]. CanDeduct(ctx context.Context, im state.Immutable, amount uint64) error diff --git a/chain/errors.go b/chain/errors.go index 2de330590d..eca4e38ee6 100644 --- a/chain/errors.go +++ b/chain/errors.go @@ -48,6 +48,8 @@ var ( ErrAuthNotActivated = errors.New("auth not activated") ErrAuthFailed = errors.New("auth failed") ErrMisalignedTime = errors.New("misaligned time") + ErrInvalidActor = errors.New("invalid actor") + ErrInvalidSponsor = errors.New("invalid sponsor") // Execution Correctness ErrInvalidBalance = errors.New("invalid balance") diff --git a/chain/mock_auth.go b/chain/mock_auth.go index b1a8405eb7..4c01f38471 100644 --- a/chain/mock_auth.go +++ b/chain/mock_auth.go @@ -39,6 +39,20 @@ func (m *MockAuth) EXPECT() *MockAuthMockRecorder { return m.recorder } +// Actor mocks base method. +func (m *MockAuth) Actor() codec.Address { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Actor") + ret0, _ := ret[0].(codec.Address) + return ret0 +} + +// Actor indicates an expected call of Actor. +func (mr *MockAuthMockRecorder) Actor() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Actor", reflect.TypeOf((*MockAuth)(nil).Actor)) +} + // AsyncVerify mocks base method. func (m *MockAuth) AsyncVerify(arg0 []byte) error { m.ctrl.T.Helper() @@ -121,20 +135,6 @@ func (mr *MockAuthMockRecorder) MaxComputeUnits(arg0 interface{}) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "MaxComputeUnits", reflect.TypeOf((*MockAuth)(nil).MaxComputeUnits), arg0) } -// Payer mocks base method. -func (m *MockAuth) Payer() []byte { - m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "Payer") - ret0, _ := ret[0].([]byte) - return ret0 -} - -// Payer indicates an expected call of Payer. -func (mr *MockAuthMockRecorder) Payer() *gomock.Call { - mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Payer", reflect.TypeOf((*MockAuth)(nil).Payer)) -} - // Refund mocks base method. func (m *MockAuth) Refund(arg0 context.Context, arg1 state.Mutable, arg2 uint64) error { m.ctrl.T.Helper() @@ -163,6 +163,20 @@ func (mr *MockAuthMockRecorder) Size() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Size", reflect.TypeOf((*MockAuth)(nil).Size)) } +// Sponsor mocks base method. +func (m *MockAuth) Sponsor() codec.Address { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Sponsor") + ret0, _ := ret[0].(codec.Address) + return ret0 +} + +// Sponsor indicates an expected call of Sponsor. +func (mr *MockAuthMockRecorder) Sponsor() *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Sponsor", reflect.TypeOf((*MockAuth)(nil).Sponsor)) +} + // StateKeys mocks base method. func (m *MockAuth) StateKeys() []string { m.ctrl.T.Helper() diff --git a/chain/transaction.go b/chain/transaction.go index d3ad0244ad..acd5729539 100644 --- a/chain/transaction.go +++ b/chain/transaction.go @@ -113,6 +113,9 @@ func (t *Transaction) Sign( func (t *Transaction) AuthAsyncVerify() func() error { return func() error { + // It is up to [t.Auth] to limit the computational + // complexity of [t.Auth.AsyncVerify] and [t.Auth.Verify] to prevent + // a DoS (invalid Auth will not charge [t.Auth.Sponsor()]. return t.Auth.AsyncVerify(t.digest) } } @@ -313,6 +316,9 @@ func (t *Transaction) PreExecute( if end >= 0 && timestamp > end { return 0, ErrAuthNotActivated } + // It is up to [t.Auth] to limit the computational + // complexity of [t.Auth.AsyncVerify] and [t.Auth.Verify] to prevent + // a DoS (invalid Auth will not charge [t.Auth.Sponsor()]. authCUs, err := t.Auth.Verify(ctx, r, im, t.Action) if err != nil { return 0, fmt.Errorf("%w: %v", ErrAuthFailed, err) //nolint:errorlint @@ -569,9 +575,8 @@ func (t *Transaction) Execute( }, nil } -// Used by mempool -func (t *Transaction) Payer() string { - return string(t.Auth.Payer()) +func (t *Transaction) Sponsor() codec.Address { + return t.Auth.Sponsor() } func (t *Transaction) Marshal(p *codec.Packer) error { @@ -692,6 +697,12 @@ func UnmarshalTx( if err != nil { return nil, fmt.Errorf("%w: could not unmarshal auth", err) } + if actorType := auth.Actor()[0]; actorType != authType { + return nil, fmt.Errorf("%w: actorType (%d) did not match authType (%d)", ErrInvalidActor, actorType, authType) + } + 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 diff --git a/cli/dependencies.go b/cli/dependencies.go index 9e1f128466..1baf5ef62d 100644 --- a/cli/dependencies.go +++ b/cli/dependencies.go @@ -4,13 +4,13 @@ package cli import ( - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" ) type Controller interface { DatabasePath() string Symbol() string Decimals() uint8 - Address(ed25519.PublicKey) string - ParseAddress(string) (ed25519.PublicKey, error) + Address(codec.Address) string + ParseAddress(string) (codec.Address, error) } diff --git a/cli/key.go b/cli/key.go index 3ede20f828..28b2d38316 100644 --- a/cli/key.go +++ b/cli/key.go @@ -7,50 +7,11 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/rpc" "github.com/ava-labs/hypersdk/utils" ) -func (h *Handler) GenerateKey() error { - // TODO: encrypt key - priv, err := ed25519.GeneratePrivateKey() - if err != nil { - return err - } - if err := h.StoreKey(priv); err != nil { - return err - } - publicKey := priv.PublicKey() - if err := h.StoreDefaultKey(publicKey); err != nil { - return err - } - utils.Outf( - "{{green}}created address:{{/}} %s", - h.c.Address(publicKey), - ) - return nil -} - -func (h *Handler) ImportKey(keyPath string) error { - priv, err := ed25519.LoadKey(keyPath) - if err != nil { - return err - } - if err := h.StoreKey(priv); err != nil { - return err - } - publicKey := priv.PublicKey() - if err := h.StoreDefaultKey(publicKey); err != nil { - return err - } - utils.Outf( - "{{green}}imported address:{{/}} %s", - h.c.Address(publicKey), - ) - return nil -} - func (h *Handler) SetKey(lookupBalance func(int, string, string, uint32, ids.ID) error) error { keys, err := h.GetKeys() if err != nil { @@ -75,7 +36,7 @@ func (h *Handler) SetKey(lookupBalance func(int, string, string, uint32, ids.ID) } utils.Outf("{{cyan}}stored keys:{{/}} %d\n", len(keys)) for i := 0; i < len(keys); i++ { - if err := lookupBalance(i, h.c.Address(keys[i].PublicKey()), uris[0], networkID, chainID); err != nil { + if err := lookupBalance(i, h.c.Address(keys[i].Address), uris[0], networkID, chainID); err != nil { return err } } @@ -86,11 +47,11 @@ func (h *Handler) SetKey(lookupBalance func(int, string, string, uint32, ids.ID) return err } key := keys[keyIndex] - return h.StoreDefaultKey(key.PublicKey()) + return h.StoreDefaultKey(key.Address) } -func (h *Handler) Balance(checkAllChains bool, promptAsset bool, printBalance func(ed25519.PublicKey, string, uint32, ids.ID, ids.ID) error) error { - priv, err := h.GetDefaultKey(true) +func (h *Handler) Balance(checkAllChains bool, promptAsset bool, printBalance func(codec.Address, string, uint32, ids.ID, ids.ID) error) error { + addr, _, err := h.GetDefaultKey(true) if err != nil { return err } @@ -117,7 +78,7 @@ func (h *Handler) Balance(checkAllChains bool, promptAsset bool, printBalance fu if err != nil { return err } - if err := printBalance(priv.PublicKey(), uri, networkID, chainID, assetID); err != nil { + if err := printBalance(addr, uri, networkID, chainID, assetID); err != nil { return err } } diff --git a/cli/prompt.go b/cli/prompt.go index c4ff4d6500..58cba562a4 100644 --- a/cli/prompt.go +++ b/cli/prompt.go @@ -11,12 +11,12 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/utils" "github.com/manifoldco/promptui" ) -func (h *Handler) PromptAddress(label string) (ed25519.PublicKey, error) { +func (h *Handler) PromptAddress(label string) (codec.Address, error) { promptText := promptui.Prompt{ Label: label, Validate: func(input string) error { @@ -29,7 +29,7 @@ func (h *Handler) PromptAddress(label string) (ed25519.PublicKey, error) { } recipient, err := promptText.Run() if err != nil { - return ed25519.EmptyPublicKey, err + return codec.EmptyAddress, err } recipient = strings.TrimSpace(recipient) return h.c.ParseAddress(recipient) diff --git a/cli/spam.go b/cli/spam.go index 4371105c4e..f9212cec9b 100644 --- a/cli/spam.go +++ b/cli/spam.go @@ -21,8 +21,8 @@ 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/crypto/ed25519" "github.com/ava-labs/hypersdk/pubsub" "github.com/ava-labs/hypersdk/rpc" "github.com/ava-labs/hypersdk/utils" @@ -47,12 +47,13 @@ var ( func (h *Handler) Spam( maxTxBacklog int, maxFee *uint64, randomRecipient bool, - createClient func(string, uint32, ids.ID), // must save on caller side - getFactory func(ed25519.PrivateKey) chain.AuthFactory, + createClient func(string, uint32, ids.ID) error, // must save on caller side + getFactory func(*PrivateKey) (chain.AuthFactory, error), + createAccount func() (*PrivateKey, error), lookupBalance func(int, string) (uint64, error), getParser func(context.Context, ids.ID) (chain.Parser, error), - getTransfer func(ed25519.PublicKey, uint64) chain.Action, - submitDummy func(*rpc.JSONRPCClient, ed25519.PrivateKey) func(context.Context, uint64) error, + getTransfer func(codec.Address, uint64) chain.Action, + submitDummy func(*rpc.JSONRPCClient, *PrivateKey) func(context.Context, uint64) error, ) error { ctx := context.Background() @@ -77,9 +78,11 @@ func (h *Handler) Spam( return err } balances := make([]uint64, len(keys)) - createClient(uris[0], networkID, chainID) + if err := createClient(uris[0], networkID, chainID); err != nil { + return err + } for i := 0; i < len(keys); i++ { - address := h.c.Address(keys[i].PublicKey()) + address := h.c.Address(keys[i].Address) balance, err := lookupBalance(i, address) if err != nil { return err @@ -92,7 +95,10 @@ func (h *Handler) Spam( } key := keys[keyIndex] balance := balances[keyIndex] - factory := getFactory(key) + factory, err := getFactory(key) + if err != nil { + return err + } // No longer using db, so we close if err := h.CloseDatabase(); err != nil { @@ -104,7 +110,7 @@ func (h *Handler) Spam( if err != nil { return err } - action := getTransfer(keys[0].PublicKey(), 0) + action := getTransfer(keys[0].Address, 0) maxUnits, err := chain.EstimateMaxUnits(parser.Rules(time.Now().UnixMilli()), action, factory, nil) if err != nil { return err @@ -145,30 +151,30 @@ func (h *Handler) Spam( utils.FormatBalance(distAmount, h.c.Decimals()), h.c.Symbol(), ) - accounts := make([]ed25519.PrivateKey, numAccounts) + accounts := make([]*PrivateKey, numAccounts) dcli, err := rpc.NewWebSocketClient(uris[0], rpc.DefaultHandshakeTimeout, pubsub.MaxPendingMessages, pubsub.MaxReadMessageSize) // we write the max read if err != nil { return err } - funds := map[ed25519.PublicKey]uint64{} + funds := map[codec.Address]uint64{} var fundsL sync.Mutex for i := 0; i < numAccounts; i++ { // Create account - pk, err := ed25519.GeneratePrivateKey() + pk, err := createAccount() if err != nil { return err } accounts[i] = pk // Send funds - _, tx, err := cli.GenerateTransactionManual(parser, nil, getTransfer(pk.PublicKey(), distAmount), factory, feePerTx) + _, tx, err := cli.GenerateTransactionManual(parser, nil, getTransfer(pk.Address, distAmount), factory, feePerTx) if err != nil { return err } if err := dcli.RegisterTx(tx); err != nil { return fmt.Errorf("%w: failed to register tx", err) } - funds[pk.PublicKey()] = distAmount + funds[pk.Address] = distAmount } for i := 0; i < numAccounts; i++ { _, dErr, result, err := dcli.ListenTx(ctx) @@ -183,6 +189,10 @@ func (h *Handler) Spam( return fmt.Errorf("%w: %s", ErrTxFailed, result.Output) } } + var recipientFunc func() (*PrivateKey, error) + if randomRecipient { + recipientFunc = createAccount + } utils.Outf("{{yellow}}distributed funds to %d accounts{{/}}\n", numAccounts) // Kickoff txs @@ -253,13 +263,16 @@ func (h *Handler) Spam( defer t.Stop() issuerIndex, issuer := getRandomIssuer(clients) - factory := getFactory(accounts[i]) + factory, err := getFactory(accounts[i]) + if err != nil { + return err + } fundsL.Lock() - balance := funds[accounts[i].PublicKey()] + balance := funds[accounts[i].Address] fundsL.Unlock() defer func() { fundsL.Lock() - funds[accounts[i].PublicKey()] = balance + funds[accounts[i].Address] = balance fundsL.Unlock() }() ut := time.Now().Unix() @@ -285,9 +298,9 @@ func (h *Handler) Spam( // Send transaction start := time.Now() - selected := map[ed25519.PublicKey]int{} + selected := map[codec.Address]int{} for k := 0; k < numTxsPerAccount; k++ { - recipient, err := getNextRecipient(randomRecipient, i, accounts) + recipient, err := getNextRecipient(i, recipientFunc, accounts) if err != nil { utils.Outf("{{orange}}failed to get next recipient:{{/}} %v\n", err) return err @@ -394,20 +407,24 @@ func (h *Handler) Spam( if maxFee != nil { feePerTx = *maxFee } - utils.Outf("{{yellow}}returning funds to %s{{/}}\n", h.c.Address(key.PublicKey())) + utils.Outf("{{yellow}}returning funds to %s{{/}}\n", h.c.Address(key.Address)) var ( returnedBalance uint64 returnsSent int ) for i := 0; i < numAccounts; i++ { - balance := funds[accounts[i].PublicKey()] + balance := funds[accounts[i].Address] if feePerTx > balance { continue } returnsSent++ // Send funds returnAmt := balance - feePerTx - _, tx, err := cli.GenerateTransactionManual(parser, nil, getTransfer(key.PublicKey(), returnAmt), getFactory(accounts[i]), feePerTx) + f, err := getFactory(accounts[i]) + if err != nil { + return err + } + _, tx, err := cli.GenerateTransactionManual(parser, nil, getTransfer(key.Address, returnAmt), f, feePerTx) if err != nil { return err } @@ -511,13 +528,14 @@ func startIssuer(cctx context.Context, issuer *txIssuer) { }() } -func getNextRecipient(randomRecipient bool, self int, keys []ed25519.PrivateKey) (ed25519.PublicKey, error) { - if randomRecipient { - priv, err := ed25519.GeneratePrivateKey() +func getNextRecipient(self int, createAccount func() (*PrivateKey, error), keys []*PrivateKey) (codec.Address, error) { + // Send to a random, new account + if createAccount != nil { + priv, err := createAccount() if err != nil { - return ed25519.EmptyPublicKey, err + return codec.EmptyAddress, err } - return priv.PublicKey(), nil + return priv.Address, nil } // Select item from array @@ -528,7 +546,7 @@ func getNextRecipient(randomRecipient bool, self int, keys []ed25519.PrivateKey) index = 0 } } - return keys[index].PublicKey(), nil + return keys[index].Address, nil } func getRandomIssuer(issuers []*txIssuer) (int, *txIssuer) { diff --git a/cli/storage.go b/cli/storage.go index b93b61fc0e..7fa2fbc700 100644 --- a/cli/storage.go +++ b/cli/storage.go @@ -9,8 +9,8 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/utils" ) @@ -67,11 +67,10 @@ func (h *Handler) GetDefaultChain(log bool) (ids.ID, []string, error) { return chainID, uris, nil } -func (h *Handler) StoreKey(privateKey ed25519.PrivateKey) error { - publicKey := privateKey.PublicKey() - k := make([]byte, 1+ed25519.PublicKeyLen) +func (h *Handler) StoreKey(priv *PrivateKey) error { + k := make([]byte, 1+codec.AddressLen) k[0] = keyPrefix - copy(k[1:], publicKey[:]) + copy(k[1:], priv.Address[:]) has, err := h.db.Has(k) if err != nil { return err @@ -79,57 +78,66 @@ func (h *Handler) StoreKey(privateKey ed25519.PrivateKey) error { if has { return ErrDuplicate } - return h.db.Put(k, privateKey[:]) + return h.db.Put(k, priv.Bytes) } -func (h *Handler) GetKey(publicKey ed25519.PublicKey) (ed25519.PrivateKey, error) { - k := make([]byte, 1+ed25519.PublicKeyLen) +func (h *Handler) GetKey(addr codec.Address) ([]byte, error) { + k := make([]byte, 1+codec.AddressLen) k[0] = keyPrefix - copy(k[1:], publicKey[:]) + copy(k[1:], addr[:]) v, err := h.db.Get(k) + // TODO: return error if not found? if errors.Is(err, database.ErrNotFound) { - return ed25519.EmptyPrivateKey, nil + return nil, nil } if err != nil { - return ed25519.EmptyPrivateKey, err + return nil, err } - return ed25519.PrivateKey(v), nil + return v, nil } -func (h *Handler) GetKeys() ([]ed25519.PrivateKey, error) { +type PrivateKey struct { + Address codec.Address + Bytes []byte +} + +func (h *Handler) GetKeys() ([]*PrivateKey, error) { iter := h.db.NewIteratorWithPrefix([]byte{keyPrefix}) defer iter.Release() - privateKeys := []ed25519.PrivateKey{} + privateKeys := []*PrivateKey{} for iter.Next() { // It is safe to use these bytes directly because the database copies the // iterator value for us. - privateKeys = append(privateKeys, ed25519.PrivateKey(iter.Value())) + privateKeys = append(privateKeys, &PrivateKey{ + Address: codec.Address(iter.Key()[1:]), + Bytes: iter.Value(), + }) } return privateKeys, iter.Error() } -func (h *Handler) StoreDefaultKey(pk ed25519.PublicKey) error { - return h.StoreDefault(defaultKeyKey, pk[:]) +func (h *Handler) StoreDefaultKey(addr codec.Address) error { + return h.StoreDefault(defaultKeyKey, addr[:]) } -func (h *Handler) GetDefaultKey(log bool) (ed25519.PrivateKey, error) { - v, err := h.GetDefault(defaultKeyKey) +func (h *Handler) GetDefaultKey(log bool) (codec.Address, []byte, error) { + raddr, err := h.GetDefault(defaultKeyKey) if err != nil { - return ed25519.EmptyPrivateKey, err + return codec.EmptyAddress, nil, err } - if len(v) == 0 { - return ed25519.EmptyPrivateKey, ErrNoKeys + if len(raddr) == 0 { + return codec.EmptyAddress, nil, ErrNoKeys } - publicKey := ed25519.PublicKey(v) - priv, err := h.GetKey(publicKey) + addr := codec.Address(raddr) + priv, err := h.GetKey(addr) if err != nil { - return ed25519.EmptyPrivateKey, err + return codec.EmptyAddress, nil, err } if log { - utils.Outf("{{yellow}}address:{{/}} %s\n", h.c.Address(publicKey)) + utils.Outf("{{yellow}}address:{{/}} %s\n", h.c.Address(addr)) } - return priv, nil + return addr, priv, nil } func (h *Handler) StoreChain(chainID ids.ID, rpc string) error { diff --git a/codec/address.go b/codec/address.go new file mode 100644 index 0000000000..4ececa92a0 --- /dev/null +++ b/codec/address.go @@ -0,0 +1,80 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package codec + +import ( + "fmt" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/avalanchego/utils/formatting/address" +) + +const ( + AddressLen = 33 + + // These consts are pulled from BIP-173: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki + fromBits = 8 + toBits = 5 + separatorLen = 1 + checksumlen = 6 + maxBech32Size = 90 +) + +type Address [AddressLen]byte + +var EmptyAddress = [AddressLen]byte{} + +// CreateAddress returns [Address] made from concatenating +// [typeID] with [id]. +func CreateAddress(typeID uint8, id ids.ID) Address { + a := make([]byte, AddressLen) + a[0] = typeID + copy(a[1:], id[:]) + return Address(a) +} + +// AddressBech32 returns a Bech32 address from [hrp] and [p]. +// This function uses avalanchego's FormatBech32 function. +func AddressBech32(hrp string, p Address) (string, error) { + expanedNum := AddressLen * fromBits + expandedLen := expanedNum / toBits + if expanedNum%toBits != 0 { + expandedLen++ + } + addrLen := len(hrp) + separatorLen + expandedLen + checksumlen + if addrLen > maxBech32Size { + return "", fmt.Errorf("%w: max=%d, requested=%d", ErrInvalidSize, maxBech32Size, addrLen) + } + return address.FormatBech32(hrp, p[:]) +} + +// MustAddressBech32 returns a Bech32 address from [hrp] and [p] or panics. +func MustAddressBech32(hrp string, p Address) string { + addr, err := AddressBech32(hrp, p) + if err != nil { + panic(err) + } + return addr +} + +// ParseAddressBech32 parses a Bech32 encoded address string and extracts +// its [AddressBytes]. If there is an error reading the address or +// the hrp value is not valid, ParseAddress returns an error. +func ParseAddressBech32(hrp, saddr string) (Address, error) { + phrp, p, err := address.ParseBech32(saddr) + if err != nil { + return EmptyAddress, err + } + if phrp != hrp { + return EmptyAddress, ErrIncorrectHRP + } + // The parsed value may be greater than [minLength] because the + // underlying Bech32 implementation requires bytes to each encode 5 bits + // instead of 8 (and we must pad the input to ensure we fill all bytes): + // https://github.com/btcsuite/btcd/blob/902f797b0c4b3af3f7196d2f5d2343931d1b2bdf/btcutil/bech32/bech32.go#L325-L331 + if len(p) < AddressLen { + return EmptyAddress, ErrInsufficientLength + } + return Address(p[:AddressLen]), nil +} diff --git a/codec/address_test.go b/codec/address_test.go new file mode 100644 index 0000000000..f372fbb630 --- /dev/null +++ b/codec/address_test.go @@ -0,0 +1,43 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package codec + +import ( + "bytes" + "testing" + + "github.com/ava-labs/avalanchego/ids" + "github.com/stretchr/testify/require" +) + +const hrp = "blah" + +func TestIDAddress(t *testing.T) { + require := require.New(t) + + id := ids.GenerateTestID() + addrBytes := CreateAddress(0, id) + addr, err := AddressBech32(hrp, addrBytes) + require.NoError(err) + + sb, err := ParseAddressBech32(hrp, addr) + require.NoError(err) + require.True(bytes.Equal(addrBytes[:], sb[:])) +} + +func TestInvalidAddressHRP(t *testing.T) { + require := require.New(t) + addr := "blah1859dz2uwazfgahey3j53ef2kqrans0c8cv4l78tda3rjkfw0txns8u2e8k" + + _, err := ParseAddressBech32("test", addr) + require.ErrorIs(err, ErrIncorrectHRP) +} + +func TestInvalidAddressChecksum(t *testing.T) { + require := require.New(t) + addr := "blah1859dz2uwazfgahey3j53ef2kqrans0c8cv4l78tda3rjkfw0txns8u2e7k" + + _, err := ParseAddressBech32(hrp, addr) + require.ErrorContains(err, "invalid checksum") +} diff --git a/codec/errors.go b/codec/errors.go index c9a05cbea5..a6b97e6440 100644 --- a/codec/errors.go +++ b/codec/errors.go @@ -6,9 +6,11 @@ package codec import "errors" var ( - ErrTooManyItems = errors.New("too many items") - ErrDuplicateItem = errors.New("duplicate item") - ErrFieldNotPopulated = errors.New("field is not populated") - ErrInvalidBitset = errors.New("invalid bitset") - ErrTooLarge = errors.New("too large") + ErrTooManyItems = errors.New("too many items") + ErrDuplicateItem = errors.New("duplicate item") + ErrFieldNotPopulated = errors.New("field is not populated") + ErrInvalidBitset = errors.New("invalid bitset") + ErrIncorrectHRP = errors.New("incorrect hrp") + ErrInsufficientLength = errors.New("insufficient length") + ErrInvalidSize = errors.New("invalid size") ) diff --git a/codec/hex.go b/codec/hex.go new file mode 100644 index 0000000000..58e4bd674a --- /dev/null +++ b/codec/hex.go @@ -0,0 +1,24 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package codec + +import "encoding/hex" + +// ToHex converts a PrivateKey to a hex string. +func ToHex(b []byte) string { + return hex.EncodeToString(b) +} + +// LoadHex Converts hex encoded string into bytes. Returns +// an error if key is invalid. +func LoadHex(s string, expectedSize int) ([]byte, error) { + bytes, err := hex.DecodeString(s) + if err != nil { + return nil, err + } + if expectedSize != -1 && len(bytes) != expectedSize { + return nil, ErrInvalidSize + } + return bytes, nil +} diff --git a/codec/optional_packer.go b/codec/optional_packer.go index 76379c1d0d..41eaea3f40 100644 --- a/codec/optional_packer.go +++ b/codec/optional_packer.go @@ -7,7 +7,6 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/crypto/ed25519" ) // OptionalPacker defines a struct that includes a Packer [ip], a bitset @@ -94,27 +93,6 @@ func (o *OptionalPacker) UnpackID(dest *ids.ID) { } } -// PackPublicKey packs [pk] into OptionalPacker if [pk] is not an empty PublicKey. -// Updates the bitset and offset accordingly. -func (o *OptionalPacker) PackPublicKey(pk ed25519.PublicKey) { - if pk == ed25519.EmptyPublicKey { - o.skipBit() - return - } - o.ip.PackPublicKey(pk) - o.setBit() -} - -// UnpackPublicKey unpacks a PublicKey into [dest] if the bitset is set at -// the current offset. Increments offset regardless. -func (o *OptionalPacker) UnpackPublicKey(dest *ed25519.PublicKey) { - if o.checkBit() { - o.ip.UnpackPublicKey(true, dest) - } else { - *dest = ed25519.EmptyPublicKey - } -} - // PackUint64 packs [l] into OptionalPacker if [l] is not an 0. // Updates the bitset and offset accordingly. func (o *OptionalPacker) PackUint64(l uint64) { @@ -155,6 +133,27 @@ func (o *OptionalPacker) UnpackInt64() int64 { return 0 } +// PackAddress packs [addr] into OptionalPacker if [addr] is not empty. +// Updates the bitset and offset accordingly. +func (o *OptionalPacker) PackAddress(addr Address) { + if addr == EmptyAddress { + o.skipBit() + return + } + o.ip.PackAddress(addr) + o.setBit() +} + +// UnpackAddress unpacks Address into [dest] if the bitset is set at +// the current offset. Increments offset regardless. +func (o *OptionalPacker) UnpackAddress(dest *Address) { + if o.checkBit() { + o.ip.UnpackAddress(dest) + } else { + *dest = EmptyAddress + } +} + // PackOptional packs an OptionalPacker in a Packer. First packs the bitset [o.b] // followed by the bytes in the OptionalPacker. func (p *Packer) PackOptional(o *OptionalPacker) { diff --git a/codec/optional_packer_test.go b/codec/optional_packer_test.go index deaadc4e6e..86cefbfb7c 100644 --- a/codec/optional_packer_test.go +++ b/codec/optional_packer_test.go @@ -4,12 +4,12 @@ package codec import ( + "bytes" "encoding/binary" "testing" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/stretchr/testify/require" ) @@ -25,56 +25,6 @@ func (o *OptionalPacker) toReader() *OptionalPacker { return pr.NewOptionalReader() } -func TestOptionalPackerWriter(t *testing.T) { - // Initializes empty writer with a limit two byte limit - require := require.New(t) - opw := NewOptionalWriter(10_000) - require.Empty(opw.ip.Bytes()) - var pubKey ed25519.PublicKey - copy(pubKey[:], TestPublicKey) - // Fill OptionalPacker - i := 0 - for i <= consts.MaxUint64Offset { - opw.PackPublicKey(pubKey) - i += 1 - } - require.Equal( - (consts.MaxUint64Offset+1)*ed25519.PublicKeyLen, - len(opw.ip.Bytes()), - "Bytes not added correctly.", - ) - require.NoError(opw.Err(), "Error packing bytes.") - opw.PackPublicKey(pubKey) - require.ErrorIs(opw.Err(), ErrTooManyItems, "Error not thrown after over packing.") -} - -func TestOptionalPackerPublicKey(t *testing.T) { - require := require.New(t) - opw := NewOptionalWriter(10_000) - var pubKey ed25519.PublicKey - copy(pubKey[:], TestPublicKey) - t.Run("Pack", func(t *testing.T) { - // Pack empty - opw.PackPublicKey(ed25519.EmptyPublicKey) - require.Empty(opw.ip.Bytes(), "PackPublickey packed an empty ID.") - // Pack ID - opw.PackPublicKey(pubKey) - require.Equal(TestPublicKey, opw.ip.Bytes(), "PackPublickey did not set bytes correctly.") - }) - t.Run("Unpack", func(t *testing.T) { - // Setup optional reader - opr := opw.toReader() - var unpackedPubkey ed25519.PublicKey - // Unpack - opr.UnpackPublicKey(&unpackedPubkey) - require.Equal(ed25519.EmptyPublicKey[:], unpackedPubkey[:], "PublicKey unpacked correctly") - opr.UnpackPublicKey(&unpackedPubkey) - require.Equal(pubKey, unpackedPubkey, "PublicKey unpacked correctly") - opr.Done() - require.NoError(opr.Err()) - }) -} - func TestOptionalPackerID(t *testing.T) { require := require.New(t) opw := NewOptionalWriter(10_000) @@ -130,6 +80,33 @@ func TestOptionalPackerUint64(t *testing.T) { }) } +func TestOptionalPackerAddress(t *testing.T) { + require := require.New(t) + opw := NewOptionalWriter(10_000) + id := ids.GenerateTestID() + addr := CreateAddress(1, id) + t.Run("Pack", func(t *testing.T) { + // Pack empty + opw.PackAddress(EmptyAddress) + require.Empty(opw.ip.Bytes(), "PackAddress packed an empty Address.") + // Pack address + opw.PackAddress(addr) + require.True(bytes.Equal(addr[:], opw.ip.Bytes()), "PackPublickey did not set bytes correctly.") + }) + t.Run("Unpack", func(t *testing.T) { + // Setup optional reader + opr := opw.toReader() + var unpackedAddr Address + // Unpack + opr.UnpackAddress(&unpackedAddr) + require.True(bytes.Equal(EmptyAddress[:], unpackedAddr[:]), "AddressBytes unpacked correctly") + opr.UnpackAddress(&unpackedAddr) + require.Equal(addr, unpackedAddr, "PublicKey unpacked correctly") + opr.Done() + require.NoError(opr.Err()) + }) +} + func TestOptionalPackerInvalidSet(t *testing.T) { require := require.New(t) opw := NewOptionalWriter(10_000) diff --git a/codec/packer.go b/codec/packer.go index ca545f6bca..2f282c81c3 100644 --- a/codec/packer.go +++ b/codec/packer.go @@ -10,13 +10,11 @@ import ( "github.com/ava-labs/avalanchego/utils/wrappers" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/window" ) // Packer is a wrapper struct for the Packer struct -// from avalanchego/utils/wrappers/packing.go. It adds methods to -// pack/unpack ids, PublicKeys and Signatures. A bool [required] parameter is +// from avalanchego/utils/wrappers/packing.go. A bool [required] parameter is // added to many unpacking methods, which signals the packer to add an error // if the expected method does not unpack properly. type Packer struct { @@ -72,14 +70,15 @@ func (p *Packer) PackFixedBytes(b []byte) { p.p.PackFixedBytes(b) } -func (p *Packer) PackShortBytes(b ShortBytes) { - l := len(b) - if l > ShortBytesMaxSize { - p.addErr(fmt.Errorf("%w: ShortBytes is too large (found=%d)", ErrTooLarge, len(b))) - return +func (p *Packer) PackAddress(a Address) { + p.p.PackFixedBytes(a[:]) +} + +func (p *Packer) UnpackAddress(dest *Address) { + copy((*dest)[:], p.p.UnpackFixedBytes(AddressLen)) + if *dest == EmptyAddress { + p.addErr(fmt.Errorf("%w: Address field is not populated", ErrFieldNotPopulated)) } - p.PackByte(uint8(l)) - p.PackFixedBytes(b) } func (p *Packer) PackBytes(b []byte) { @@ -90,11 +89,6 @@ func (p *Packer) UnpackFixedBytes(size int, dest *[]byte) { copy((*dest), p.p.UnpackFixedBytes(size)) } -func (p *Packer) UnpackShortBytes(dest *ShortBytes) { - l := int(p.p.UnpackByte()) - *dest = p.p.UnpackFixedBytes(l) -} - // UnpackBytes unpacks [limit] bytes into [dest]. Otherwise // if [limit] >= 0, UnpackBytes unpacks a byte slice array into [dest]. If // [required] is set to true and the amount of bytes written to [dest] is 0, @@ -134,31 +128,6 @@ func (p *Packer) UnpackInt64(required bool) int64 { return int64(v) } -func (p *Packer) PackPublicKey(src ed25519.PublicKey) { - p.p.PackFixedBytes(src[:]) -} - -// UnpackPublicKey ed25519.PublicKey into [dest]. -func (p *Packer) UnpackPublicKey(required bool, dest *ed25519.PublicKey) { - copy((*dest)[:], p.p.UnpackFixedBytes(ed25519.PublicKeyLen)) - if required && *dest == ed25519.EmptyPublicKey { - p.addErr(fmt.Errorf("%w: PublicKey field is not populated", ErrFieldNotPopulated)) - } -} - -func (p *Packer) PackSignature(src ed25519.Signature) { - p.p.PackFixedBytes(src[:]) -} - -// UnpackPublicKey ed25519.Signature into [dest]. -// TODO: should add required param? -func (p *Packer) UnpackSignature(dest *ed25519.Signature) { - copy((*dest)[:], p.p.UnpackFixedBytes(ed25519.SignatureLen)) - if *dest == ed25519.EmptySignature { - p.addErr(fmt.Errorf("%w: Signature field is not populated", ErrFieldNotPopulated)) - } -} - func (p *Packer) PackInt(v int) { p.p.PackInt(uint32(v)) } diff --git a/codec/packer_test.go b/codec/packer_test.go index ab0dc9d85d..06daeeb739 100644 --- a/codec/packer_test.go +++ b/codec/packer_test.go @@ -8,27 +8,13 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/window" "github.com/stretchr/testify/require" ) var ( - TestPublicKey = []byte{ - 115, 50, 124, 153, 59, 53, 196, 150, 168, 143, 151, 235, - 222, 128, 136, 161, 9, 40, 139, 85, 182, 153, 68, 135, - 62, 166, 45, 235, 251, 246, 69, 7, - } - TestString = "TestString" - TestBool = true - TestSignature = []byte{ - 2, 8, 143, 126, 80, 159, 186, 93, 157, - 97, 183, 80, 183, 86, 3, 128, 223, 79, 164, 21, 51, 88, - 224, 186, 134, 18, 209, 100, 166, 37, 132, 237, 48, 49, - 102, 144, 53, 111, 245, 209, 141, 252, 154, 0, 111, 229, - 175, 23, 122, 55, 166, 97, 166, 228, 68, 247, 23, 113, - 32, 247, 254, 190, 203, 8, - } + TestString = "TestString" + TestBool = true TestWindow = []byte{1, 2, 3, 4, 5} ) @@ -46,57 +32,6 @@ func TestNewWriter(t *testing.T) { require.Error(wr.Err(), "Error not set.") } -func TestPackerPublicKey(t *testing.T) { - require := require.New(t) - wp := NewWriter(ed25519.PublicKeyLen, ed25519.PublicKeyLen) - var pubKey ed25519.PublicKey - copy(pubKey[:], TestPublicKey) - t.Run("Pack", func(t *testing.T) { - // Pack - require.Len(pubKey, ed25519.PublicKeyLen) - wp.PackPublicKey(pubKey) - require.Equal(TestPublicKey, wp.Bytes(), "PublicKey not packed correctly.") - require.NoError(wp.Err(), "Error packing PublicKey.") - }) - t.Run("Unpack", func(t *testing.T) { - // Unpack - rp := NewReader(wp.Bytes(), ed25519.PublicKeyLen) - require.Equal(wp.Bytes(), rp.Bytes(), "Reader not initialized correctly.") - var unpackedPubKey ed25519.PublicKey - rp.UnpackPublicKey(true, &unpackedPubKey) - require.Equal(pubKey, unpackedPubKey, "UnpackPublicKey unpacked incorrectly.") - require.NoError(rp.Err(), "UnpackPublicKey set an error.") - // Unpack again - rp.UnpackPublicKey(true, &unpackedPubKey) - require.Error(rp.Err(), "UnpackPublicKey did not set error.") - }) -} - -func TestPackerSignature(t *testing.T) { - require := require.New(t) - wp := NewWriter(ed25519.SignatureLen, ed25519.SignatureLen) - var sig ed25519.Signature - copy(sig[:], TestSignature) - t.Run("Pack", func(t *testing.T) { - // Pack - wp.PackSignature(sig) - require.Equal(TestSignature, wp.Bytes()) - require.NoError(wp.Err(), "Error packing Signature.") - }) - t.Run("Unpack", func(t *testing.T) { - // Unpack - rp := NewReader(wp.Bytes(), ed25519.SignatureLen) - require.Equal(wp.Bytes(), rp.Bytes(), "Reader not initialized correctly.") - var unpackedSig ed25519.Signature - rp.UnpackSignature(&unpackedSig) - require.Equal(sig, unpackedSig, "UnpackSignature unpacked incorrectly.") - require.NoError(rp.Err(), "UnpackSignature set an error.") - // Unpack again - rp.UnpackSignature(&unpackedSig) - require.Error(rp.Err(), "UnpackPublicKey did not set error.") - }) -} - func TestPackerID(t *testing.T) { require := require.New(t) wp := NewWriter(consts.IDLen, consts.IDLen) @@ -154,33 +89,28 @@ func TestPackerWindow(t *testing.T) { }) } -func TestPackerShortBytes(t *testing.T) { +func TestPackerAddress(t *testing.T) { require := require.New(t) - t.Run("Pack Too Large", func(t *testing.T) { - wp := NewWriter(1024, 1024) - wp.PackShortBytes(make([]byte, 1024)) - require.ErrorIs(wp.Err(), ErrTooLarge) - }) - wp := NewWriter(ed25519.PublicKeyLen+1, ed25519.PublicKeyLen+1) - pubKey := make(ShortBytes, ed25519.PublicKeyLen) - copy(pubKey[:], TestPublicKey) + wp := NewWriter(AddressLen, AddressLen) + id := ids.GenerateTestID() + addr := CreateAddress(1, id) t.Run("Pack", func(t *testing.T) { // Pack - wp.PackShortBytes(pubKey) + wp.PackAddress(addr) b := wp.Bytes() - require.NoError(wp.Err(), "Error packing PublicKey.") - require.Len(b, ed25519.PublicKeyLen+1) - require.Equal(uint8(ed25519.PublicKeyLen), b[0], "PublicKeyLen not packed correctly.") - require.Equal(TestPublicKey, b[1:], "PublicKey not packed correctly.") + require.NoError(wp.Err()) + require.Len(b, AddressLen) + require.Equal(uint8(1), b[0]) + require.Equal(id[:], b[1:]) }) t.Run("Unpack", func(t *testing.T) { // Unpack - rp := NewReader(wp.Bytes(), ed25519.PublicKeyLen+1) - require.Equal(wp.Bytes(), rp.Bytes(), "Reader not initialized correctly.") - var unpackedPubKey ShortBytes - rp.UnpackShortBytes(&unpackedPubKey) - require.Equal(pubKey, unpackedPubKey, "UnpackPublicKey unpacked incorrectly.") - require.NoError(rp.Err(), "UnpackPublicKey set an error.") + rp := NewReader(wp.Bytes(), AddressLen) + require.Equal(wp.Bytes(), rp.Bytes()) + var unpackedAddr Address + rp.UnpackAddress(&unpackedAddr) + require.Equal(addr[:], unpackedAddr[:]) + require.NoError(rp.Err()) }) } diff --git a/codec/types.go b/codec/types.go deleted file mode 100644 index 07a457f3a3..0000000000 --- a/codec/types.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package codec - -import "github.com/ava-labs/hypersdk/consts" - -const ShortBytesMaxSize = int(consts.MaxUint8) - -type ShortBytes []byte diff --git a/codec/utils.go b/codec/utils.go index 87dc17a67b..fffce7d533 100644 --- a/codec/utils.go +++ b/codec/utils.go @@ -17,14 +17,6 @@ func CummSize[T SizeType](arr []T) int { return size } -func ShortBytesLen(msg []byte) int { - return consts.ByteLen + len(msg) -} - -func ShortBytesLenSize(msgSize int) int { - return consts.ByteLen + msgSize -} - func BytesLen(msg []byte) int { return consts.IntLen + len(msg) } diff --git a/config/config.go b/config/config.go index c14499b65e..ee5d48f537 100644 --- a/config/config.go +++ b/config/config.go @@ -10,25 +10,26 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/profiler" "github.com/ava-labs/avalanchego/utils/units" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/trace" ) type Config struct{} -func (c *Config) GetLogLevel() logging.Level { return logging.Info } -func (c *Config) GetSignatureVerificationCores() int { return 1 } -func (c *Config) GetRootGenerationCores() int { return 1 } -func (c *Config) GetTransactionExecutionCores() int { return 1 } -func (c *Config) GetMempoolSize() int { return 2_048 } -func (c *Config) GetMempoolPayerSize() int { return 32 } -func (c *Config) GetMempoolExemptPayers() [][]byte { return nil } -func (c *Config) GetStreamingBacklogSize() int { return 1024 } -func (c *Config) GetStateEvictionBatchSize() int { return 4 * units.MiB } -func (c *Config) GetIntermediateNodeCacheSize() int { return 4 * units.GiB } -func (c *Config) GetValueNodeCacheSize() int { return 2 * units.GiB } -func (c *Config) GetTraceConfig() *trace.Config { return &trace.Config{Enabled: false} } -func (c *Config) GetStateSyncParallelism() int { return 4 } -func (c *Config) GetStateSyncServerDelay() time.Duration { return 0 } // used for testing +func (c *Config) GetLogLevel() logging.Level { return logging.Info } +func (c *Config) GetSignatureVerificationCores() int { return 1 } +func (c *Config) GetRootGenerationCores() int { return 1 } +func (c *Config) GetTransactionExecutionCores() int { return 1 } +func (c *Config) GetMempoolSize() int { return 2_048 } +func (c *Config) GetMempoolSponsorSize() int { return 32 } +func (c *Config) GetMempoolExemptSponsors() []codec.Address { return nil } +func (c *Config) GetStreamingBacklogSize() int { return 1024 } +func (c *Config) GetStateEvictionBatchSize() int { return 4 * units.MiB } +func (c *Config) GetIntermediateNodeCacheSize() int { return 4 * units.GiB } +func (c *Config) GetValueNodeCacheSize() int { return 2 * units.GiB } +func (c *Config) GetTraceConfig() *trace.Config { return &trace.Config{Enabled: false} } +func (c *Config) GetStateSyncParallelism() int { return 4 } +func (c *Config) GetStateSyncServerDelay() time.Duration { return 0 } // used for testing func (c *Config) GetParsedBlockCacheSize() int { return 128 } func (c *Config) GetStateHistoryLength() int { return 256 } diff --git a/crypto/ed25519/ed25519.go b/crypto/ed25519/ed25519.go index 70a47d7105..184284a419 100644 --- a/crypto/ed25519/ed25519.go +++ b/crypto/ed25519/ed25519.go @@ -5,13 +5,10 @@ package ed25519 import ( "crypto/rand" - "encoding/hex" - "os" "github.com/oasisprotocol/curve25519-voi/primitives/ed25519" "github.com/oasisprotocol/curve25519-voi/primitives/ed25519/extra/cache" - "github.com/ava-labs/avalanchego/utils/formatting/address" "github.com/ava-labs/hypersdk/crypto" ) @@ -66,35 +63,6 @@ func init() { cacheVerifier = NewVerifier(cache.NewLRUCache(cacheSize)) } -// Address returns a Bech32 address from hrp and p. -// This function uses avalanchego's FormatBech32 function. -func Address(hrp string, p PublicKey) string { - // TODO: handle error - addrString, _ := address.FormatBech32(hrp, p[:]) - return addrString -} - -// ParseAddress parses a Bech32 encoded address string and extracts -// its public key. If there is an error reading the address or the hrp -// value is not valid, ParseAddress returns an EmptyPublicKey and error. -func ParseAddress(hrp, saddr string) (PublicKey, error) { - phrp, pk, err := address.ParseBech32(saddr) - if err != nil { - return EmptyPublicKey, err - } - if phrp != hrp { - return EmptyPublicKey, crypto.ErrIncorrectHrp - } - // The parsed public key may be greater than [PublicKeyLen] because the - // underlying Bech32 implementation requires bytes to each encode 5 bits - // instead of 8 (and we must pad the input to ensure we fill all bytes): - // https://github.com/btcsuite/btcd/blob/902f797b0c4b3af3f7196d2f5d2343931d1b2bdf/btcutil/bech32/bech32.go#L325-L331 - if len(pk) < PublicKeyLen { - return EmptyPublicKey, crypto.ErrInvalidPublicKey - } - return PublicKey(pk[:PublicKeyLen]), nil -} - // GeneratePrivateKey returns a Ed25519 PrivateKey. func GeneratePrivateKey() (PrivateKey, error) { _, k, err := ed25519.GenerateKey(nil) @@ -110,31 +78,6 @@ func (p PrivateKey) PublicKey() PublicKey { return PublicKey(p[PrivateKeySeedLen:]) } -// ToHex converts a PrivateKey to a hex string. -func (p PrivateKey) ToHex() string { - return hex.EncodeToString(p[:]) -} - -// Save writes [PrivateKey] to a file [filename]. If filename does -// not exist, it creates a new file with read/write permissions (0o600). -func (p PrivateKey) Save(filename string) error { - return os.WriteFile(filename, p[:], 0o600) -} - -// LoadKey returns a PrivateKey from a file filename. -// If there is an error reading the file, or the file contains an -// invalid PrivateKey, LoadKey returns an EmptyPrivateKey and an error. -func LoadKey(filename string) (PrivateKey, error) { - bytes, err := os.ReadFile(filename) - if err != nil { - return EmptyPrivateKey, err - } - if len(bytes) != ed25519.PrivateKeySize { - return EmptyPrivateKey, crypto.ErrInvalidPrivateKey - } - return PrivateKey(bytes), nil -} - // Sign returns a valid signature for msg using pk. func Sign(msg []byte, pk PrivateKey) Signature { sig := ed25519.Sign(pk[:], msg) @@ -146,19 +89,6 @@ func Verify(msg []byte, p PublicKey, s Signature) bool { return cacheVerifier.VerifyWithOptions(p[:], msg, s[:], &verifyOptions) } -// HexToKey Converts a hexadecimal encoded key into a PrivateKey. Returns -// an EmptyPrivateKey and error if key is invalid. -func HexToKey(key string) (PrivateKey, error) { - bytes, err := hex.DecodeString(key) - if err != nil { - return EmptyPrivateKey, err - } - if len(bytes) != ed25519.PrivateKeySize { - return EmptyPrivateKey, crypto.ErrInvalidPrivateKey - } - return PrivateKey(bytes), nil -} - func CachePublicKey(p PublicKey) { cacheVerifier.AddPublicKey(p[:]) } diff --git a/crypto/ed25519/ed25519_test.go b/crypto/ed25519/ed25519_test.go index 3cf2c3d8da..7733f5b9e9 100644 --- a/crypto/ed25519/ed25519_test.go +++ b/crypto/ed25519/ed25519_test.go @@ -6,12 +6,9 @@ package ed25519 import ( "crypto/ed25519" "crypto/rand" - "os" - "path/filepath" "strconv" "testing" - "github.com/ava-labs/hypersdk/crypto" oed25519 "github.com/oasisprotocol/curve25519-voi/primitives/ed25519" "github.com/oasisprotocol/curve25519-voi/primitives/ed25519/extra/cache" @@ -34,12 +31,6 @@ var ( 222, 128, 136, 161, 9, 40, 139, 85, 182, 153, 68, 135, 62, 166, 45, 235, 251, 246, 69, 7, } - TestHRP = "test" - TestAddressString = "test1wve8exfmxhzfd2y0jl4aaqyg5yyj3z6" + - "4k6v5fpe75ck7h7lkg5rsf7cgc6" // this is the address associated with TestPublicKey and TestHRP - TestPrivateKeyHex = "20f176ded20da48003126dd7b0d7a8abc" + - "2b5040bfdc7adf06b947fbe30a40c3073327c993b35c496a88f97eb" + - "de8088a109288b55b69944873ea62debfbf64507" ) func TestGeneratePrivateKeyFormat(t *testing.T) { @@ -88,123 +79,6 @@ func TestPublicKeyFormat(t *testing.T) { require.Len(pubKey, PublicKeyLen, "PublicKey has incorrect length") } -func TestAddress(t *testing.T) { - require := require.New(t) - var pubKey PublicKey - copy(pubKey[:], TestPublicKey) - addr := Address(TestHRP, pubKey) - require.Equal(addr, TestAddressString, "Unexpected Address") -} - -func TestParseAddressIncorrectHrt(t *testing.T) { - require := require.New(t) - pubkey, err := ParseAddress("wronghrt", TestAddressString) - require.ErrorIs(err, crypto.ErrIncorrectHrp, "ErrIncorrectHrp not returned") - require.Equal( - pubkey, - PublicKey(EmptyPublicKey), - "Unexpected PublicKey from ParseAddress", - ) -} - -func TestParseAddressIncorrectSaddr(t *testing.T) { - require := require.New(t) - - pubkey, err := ParseAddress(TestHRP, "incorrecttestaddressstring") - require.Error(err, "Error was not thrown after call with incorrect parameters") - require.Equal( - pubkey, - PublicKey(EmptyPublicKey), - "Unexpected PublicKey from ParseAddress", - ) -} - -func TestParseAddress(t *testing.T) { - require := require.New(t) - - var expectedPubkey PublicKey - copy(expectedPubkey[:], TestPublicKey) - pubkey, err := ParseAddress(TestHRP, TestAddressString) - require.NoError(err, "Error returned by ParseAddress") - require.Equal(pubkey, expectedPubkey, "Unexpected PublicKey from ParseAddress") -} - -func TestSaveKey(t *testing.T) { - require := require.New(t) - - tempDir := os.TempDir() - filename := filepath.Join(tempDir, "SaveKey") - - err := TestPrivateKey.Save(filename) - require.NoError(err, "Error during call to SaveKey") - require.FileExists(filename, "SaveKey did not create file") - // Check correct key was saved in file - bytes, err := os.ReadFile(filename) - var privKey PrivateKey - copy(privKey[:], bytes) - require.NoError(err, "Reading saved file threw an error") - require.Equal(TestPrivateKey, privKey, "Key is different than saved key") - // Remove File - _ = os.Remove(filename) -} - -func TestLoadKeyIncorrectKey(t *testing.T) { - // Creates dummy file with invalid key size - // Checks that LoadKey returns emptyprivatekey and err - require := require.New(t) - invalidPrivKey := []byte{1, 2, 3, 4, 5} - - // Writes - f, err := os.CreateTemp("", "TestLoadKey*") - require.NoError(err) - fileName := f.Name() - - err = os.WriteFile(fileName, invalidPrivKey, 0o600) - require.NoError(err, "Error writing using OS during tests") - err = f.Close() - require.NoError(err, "Error closing file during tests") - - privKey, err := LoadKey(fileName) - - // Validate - require.ErrorIs(err, crypto.ErrInvalidPrivateKey, - "ErrInvalidPrivateKey was not returned") - require.Equal(privKey, PrivateKey(EmptyPrivateKey)) - - // Remove file - _ = os.Remove(fileName) -} - -func TestLoadKeyInvalidFile(t *testing.T) { - require := require.New(t) - - filename := "FileNameDoesntExist" - privKey, err := LoadKey(filename) - require.Error(err, "Error was not returned") - require.Equal(privKey, PrivateKey(EmptyPrivateKey), - "EmptyPrivateKey was not returned") -} - -func TestLoadKey(t *testing.T) { - require := require.New(t) - // Creates dummy file with valid key size - // Checks the returned value was the key in the file - f, err := os.CreateTemp("", "TestLoadKey*") - require.NoError(err) - fileName := f.Name() - - _, err = f.Write(TestPrivateKey[:]) - require.NoError(err) - err = f.Close() - require.NoError(err) - - privKey, err := LoadKey(fileName) - // Validate - require.NoError(err, "Error was incorrectly returned during LoadKey") - require.Equal(privKey, TestPrivateKey, "PrivateKey was different than expected") - _ = os.Remove(fileName) -} - func TestSignSignatureValid(t *testing.T) { require := require.New(t) @@ -238,26 +112,6 @@ func TestVerifyInvalidParams(t *testing.T) { "Verify incorrectly verified a message") } -func TestHexToKeyInvalidKey(t *testing.T) { - require := require.New(t) - invalidHex := "1234" - hex, err := HexToKey(invalidHex) - require.ErrorIs(err, crypto.ErrInvalidPrivateKey, "Incorrect error returned") - require.Equal(PrivateKey(EmptyPrivateKey), hex) -} - -func TestHexToKey(t *testing.T) { - require := require.New(t) - hex, err := HexToKey(TestPrivateKeyHex) - require.NoError(err, "Incorrect error returned") - require.Equal(TestPrivateKey, hex) -} - -func TestKeyToHex(t *testing.T) { - require := require.New(t) - require.Equal(TestPrivateKeyHex, TestPrivateKey.ToHex()) -} - func BenchmarkStdLibVerifySingle(b *testing.B) { for i := 0; i < b.N; i++ { b.StopTimer() diff --git a/crypto/errors.go b/crypto/errors.go index 5eb06d85f3..eb6dc556c3 100644 --- a/crypto/errors.go +++ b/crypto/errors.go @@ -8,6 +8,5 @@ import "errors" var ( ErrInvalidPrivateKey = errors.New("invalid private key") ErrInvalidPublicKey = errors.New("invalid public key") - ErrIncorrectHrp = errors.New("incorrect hrp") ErrInvalidSignature = errors.New("invalid signature") ) diff --git a/crypto/secp256r1/secp256r1.go b/crypto/secp256r1/secp256r1.go index a57d99fd70..1e94f4f47e 100644 --- a/crypto/secp256r1/secp256r1.go +++ b/crypto/secp256r1/secp256r1.go @@ -8,14 +8,10 @@ import ( "crypto/elliptic" "crypto/rand" "crypto/sha256" - "encoding/hex" "errors" "fmt" "math/big" - "os" - "github.com/ava-labs/avalanchego/utils/formatting/address" - "github.com/ava-labs/hypersdk/crypto" "golang.org/x/crypto/cryptobyte" "golang.org/x/crypto/cryptobyte/asn1" ) @@ -101,31 +97,6 @@ func (p PrivateKey) PublicKey() PublicKey { return PublicKey(elliptic.MarshalCompressed(elliptic.P256(), x, y)) } -// ToHex converts a PrivateKey to a hex string. -func (p PrivateKey) ToHex() string { - return hex.EncodeToString(p[:]) -} - -// Save writes [PrivateKey] to a file [filename]. If filename does -// not exist, it creates a new file with read/write permissions (0o600). -func (p PrivateKey) Save(filename string) error { - return os.WriteFile(filename, p[:], 0o600) -} - -// LoadKey returns a PrivateKey from a file filename. -// If there is an error reading the file, or the file contains an -// invalid PrivateKey, LoadKey returns an EmptyPrivateKey and an error. -func LoadKey(filename string) (PrivateKey, error) { - bytes, err := os.ReadFile(filename) - if err != nil { - return EmptyPrivateKey, err - } - if len(bytes) != PrivateKeyLen { - return EmptyPrivateKey, crypto.ErrInvalidPrivateKey - } - return PrivateKey(bytes), nil -} - // generateSignature creates a valid signature, potentially padding // r and/or s with zeros. // @@ -218,19 +189,6 @@ func Verify(msg []byte, p PublicKey, sig Signature) bool { return ecdsa.Verify(pk, digest[:], r, s) } -// HexToKey Converts a hexadecimal encoded key into a PrivateKey. Returns -// an EmptyPrivateKey and error if key is invalid. -func HexToKey(key string) (PrivateKey, error) { - bytes, err := hex.DecodeString(key) - if err != nil { - return EmptyPrivateKey, err - } - if len(bytes) != PrivateKeyLen { - return EmptyPrivateKey, crypto.ErrInvalidPrivateKey - } - return PrivateKey(bytes), nil -} - // ParseASN1Signature parses an ASN.1 encoded (using DER serialization) secp256r1 signature. // This function does not normalize the extracted signature. // @@ -248,35 +206,6 @@ func ParseASN1Signature(sig []byte) (r, s []byte, err error) { return r, s, nil } -// Address returns a Bech32 address from hrp and p. -// This function uses avalanchego's FormatBech32 function. -func Address(hrp string, p PublicKey) string { - // TODO: handle error - addrString, _ := address.FormatBech32(hrp, p[:]) - return addrString -} - -// ParseAddress parses a Bech32 encoded address string and extracts -// its public key. If there is an error reading the address or the hrp -// value is not valid, ParseAddress returns an EmptyPublicKey and error. -func ParseAddress(hrp, saddr string) (PublicKey, error) { - phrp, pk, err := address.ParseBech32(saddr) - if err != nil { - return EmptyPublicKey, err - } - if phrp != hrp { - return EmptyPublicKey, crypto.ErrIncorrectHrp - } - // The parsed public key may be greater than [PublicKeyLen] because the - // underlying Bech32 implementation requires bytes to each encode 5 bits - // instead of 8 (and we must pad the input to ensure we fill all bytes): - // https://github.com/btcsuite/btcd/blob/902f797b0c4b3af3f7196d2f5d2343931d1b2bdf/btcutil/bech32/bech32.go#L325-L331 - if len(pk) < PublicKeyLen { - return EmptyPublicKey, crypto.ErrInvalidPublicKey - } - return PublicKey(pk[:PublicKeyLen]), nil -} - // used for testing func denormalizedSign(msg []byte, pk PrivateKey) (Signature, error) { for { diff --git a/eheap/eheap_test.go b/eheap/eheap_test.go index 09d9383d86..fef8a6d6fa 100644 --- a/eheap/eheap_test.go +++ b/eheap/eheap_test.go @@ -11,11 +11,11 @@ import ( "github.com/ava-labs/avalanchego/ids" ) -const testPayer = "testPayer" +const testSponsor = "testSponsor" type TestItem struct { id ids.ID - payer string + sponsor string timestamp int64 } @@ -23,19 +23,19 @@ func (mti *TestItem) ID() ids.ID { return mti.id } -func (mti *TestItem) Payer() string { - return mti.payer +func (mti *TestItem) Sponsor() string { + return mti.sponsor } func (mti *TestItem) Expiry() int64 { return mti.timestamp } -func GenerateTestItem(payer string, t int64) *TestItem { +func GenerateTestItem(sponsor string, t int64) *TestItem { id := ids.GenerateTestID() return &TestItem{ id: id, - payer: payer, + sponsor: sponsor, timestamp: t, } } @@ -51,7 +51,7 @@ func TestExpiryHeapAdd(t *testing.T) { // Adds to the mempool. require := require.New(t) eheap := New[*TestItem](0) - item := GenerateTestItem("payer", 1) + item := GenerateTestItem("sponsor", 1) eheap.Add(item) require.Equal(eheap.minHeap.Len(), 1, "MinHeap not pushed correctly") require.True(eheap.minHeap.Has(item.ID()), "MinHeap does not have ID") @@ -61,7 +61,7 @@ func TestExpiryHeapRemove(t *testing.T) { // Removes from the mempool. require := require.New(t) eheap := New[*TestItem](0) - item := GenerateTestItem("payer", 1) + item := GenerateTestItem("sponsor", 1) // Add first eheap.Add(item) require.Equal(eheap.minHeap.Len(), 1, "MinHeap not pushed correctly") @@ -77,7 +77,7 @@ func TestExpiryHeapRemoveEmpty(t *testing.T) { // Removes from the mempool. require := require.New(t) eheap := New[*TestItem](0) - item := GenerateTestItem("payer", 1) + item := GenerateTestItem("sponsor", 1) // Require this returns eheap.Remove(item.ID()) require.True(true, "not true") @@ -85,10 +85,10 @@ func TestExpiryHeapRemoveEmpty(t *testing.T) { func TestSetMin(t *testing.T) { require := require.New(t) - payer := "payer" + sponsor := "sponsor" eheap := New[*TestItem](0) for i := int64(0); i <= 9; i++ { - item := GenerateTestItem(payer, i) + item := GenerateTestItem(sponsor, i) eheap.Add(item) require.True(eheap.Has(item.ID()), "TX not included") } @@ -109,11 +109,11 @@ func TestSetMin(t *testing.T) { func TestSetMinRemovesAll(t *testing.T) { require := require.New(t) - payer := "payer" + sponsor := "sponsor" eheap := New[*TestItem](0) var items []*TestItem for i := int64(0); i <= 4; i++ { - item := GenerateTestItem(payer, i) + item := GenerateTestItem(sponsor, i) items = append(items, item) eheap.Add(item) require.True(eheap.Has(item.ID()), "TX not included") @@ -129,9 +129,9 @@ func TestPeekMin(t *testing.T) { require := require.New(t) eheap := New[*TestItem](0) - itemMin := GenerateTestItem(testPayer, 1) - itemMed := GenerateTestItem(testPayer, 2) - itemMax := GenerateTestItem(testPayer, 3) + itemMin := GenerateTestItem(testSponsor, 1) + itemMed := GenerateTestItem(testSponsor, 2) + itemMax := GenerateTestItem(testSponsor, 3) min, ok := eheap.PeekMin() require.False(ok) require.Nil(min, "Peek UnitPrice is incorrect") @@ -160,9 +160,9 @@ func TestPopMin(t *testing.T) { eheap := New[*TestItem](0) - itemMin := GenerateTestItem(testPayer, 1) - itemMed := GenerateTestItem(testPayer, 2) - itemMax := GenerateTestItem(testPayer, 3) + itemMin := GenerateTestItem(testSponsor, 1) + itemMed := GenerateTestItem(testSponsor, 2) + itemMax := GenerateTestItem(testSponsor, 3) min, ok := eheap.PopMin() require.False(ok) require.Nil(min, "Pop value is incorrect") @@ -185,7 +185,7 @@ func TestHas(t *testing.T) { require := require.New(t) eheap := New[*TestItem](0) - item := GenerateTestItem(testPayer, 1) + item := GenerateTestItem(testSponsor, 1) require.False(eheap.Has(item.ID()), "Found an item that was not added.") eheap.Add(item) require.True(eheap.Has(item.ID()), "Did not find item.") @@ -196,7 +196,7 @@ func TestLen(t *testing.T) { eheap := New[*TestItem](0) for i := int64(0); i <= 4; i++ { - item := GenerateTestItem(testPayer, i) + item := GenerateTestItem(testSponsor, i) eheap.Add(item) require.True(eheap.Has(item.ID()), "TX not included") } diff --git a/examples/morpheusvm/README.md b/examples/morpheusvm/README.md index ef54d1c92a..a937b86cdb 100644 --- a/examples/morpheusvm/README.md +++ b/examples/morpheusvm/README.md @@ -47,7 +47,7 @@ use the following command to terminate: ./scripts/stop.sh; ``` -_By default, this allocates all funds on the network to `morpheus1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsp30ucp`. The private +_By default, this allocates all funds on the network to `morpheus1qrzvk4zlwj9zsacqgtufx7zvapd3quufqpxk5rsdd4633m4wz2fdjk97rwu`. The private key for this address is `0x323b1d8f4eed5f0da9da93071b034f2dce9d2d22692c172f3cb252a64ddfafd01b057de320297c29ad0c1f589ea216869cf1938d88c9fbd70d6748323dbf2fa7`. For convenience, this key has is also stored at `demo.pk`._ @@ -64,13 +64,13 @@ _This command will put the compiled CLI in `./build/morpheus-cli`._ Next, you'll need to add the chains you created and the default key to the `morpheus-cli`. You can use the following commands from this location to do so: ```bash -./build/morpheus-cli key import demo.pk +./build/morpheus-cli key import ed25519 demo.pk ``` If the key is added corretcly, you'll see the following log: ``` database: .morpheus-cli -imported address: morpheus1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsp30ucp +imported address: morpheus1qrzvk4zlwj9zsacqgtufx7zvapd3quufqpxk5rsdd4633m4wz2fdjk97rwu ``` Next, you'll need to store the URLs of the nodes running on your Subnet: @@ -103,7 +103,7 @@ following command to get the current balance of the key you added: If successful, the balance response should look like this: ``` database: .morpheus-cli -address: morpheus1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsp30ucp +address:morpheus1qrzvk4zlwj9zsacqgtufx7zvapd3quufqpxk5rsdd4633m4wz2fdjk97rwu chainID: 2mQy8Q9Af9dtZvVM8pKsh2rB3cT3QNLjghpet5Mm5db4N7Hwgk uri: http://127.0.0.1:45778/ext/bc/2mQy8Q9Af9dtZvVM8pKsh2rB3cT3QNLjghpet5Mm5db4N7Hwgk balance: 1000.000000000 RED @@ -114,13 +114,13 @@ Now that we have a balance to send, we need to generate another address to send we use bech32 addresses, we can't just put a random string of characters as the recipient (won't pass checksum test that protects users from sending to off-by-one addresses). ```bash -./build/morpheus-cli key generate +./build/morpheus-cli key generate secp256r1 ``` If successful, the `morpheus-cli` will emit the new address: ``` database: .morpheus-cli -created address: morpheus1s3ukd2gnhxl96xa5spzg69w7qd2x4ypve0j5vm0qflvlqr4na5zsezaf2f +created address: morpheus1q8rc050907hx39vfejpawjydmwe6uujw0njx9s6skzdpp3cm2he5s036p07 ``` By default, the `morpheus-cli` sets newly generated addresses to be the default. We run @@ -134,8 +134,8 @@ You should see something like this: database: .morpheus-cli chainID: 2mQy8Q9Af9dtZvVM8pKsh2rB3cT3QNLjghpet5Mm5db4N7Hwgk stored keys: 2 -0) address: morpheus1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsp30ucp balance: 1000.000000000 RED -1) address: morpheus1s3ukd2gnhxl96xa5spzg69w7qd2x4ypve0j5vm0qflvlqr4na5zsezaf2f balance: 0.000000000 RED +0) address (ed25519): morpheus1qrzvk4zlwj9zsacqgtufx7zvapd3quufqpxk5rsdd4633m4wz2fdjk97rwu balance: 10000000000.000000000 RED +1) address (secp256r1): morpheus1q8rc050907hx39vfejpawjydmwe6uujw0njx9s6skzdpp3cm2he5s036p07 balance: 0.000000000 RED set default key: 0 ``` @@ -148,10 +148,10 @@ Lastly, we trigger the transfer: The `morpheus-cli` will emit the following logs when the transfer is successful: ``` database: .morpheus-cli -address: morpheus1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsp30ucp +address: morpheus1qqds2l0ryq5hc2ddps04384zz6rfeuvn3kyvn77hp4n5sv3ahuh6wgkt57y chainID: 2mQy8Q9Af9dtZvVM8pKsh2rB3cT3QNLjghpet5Mm5db4N7Hwgk balance: 1000.000000000 RED -recipient: morpheus1s3ukd2gnhxl96xa5spzg69w7qd2x4ypve0j5vm0qflvlqr4na5zsezaf2f +recipient: morpheus1q8rc050907hx39vfejpawjydmwe6uujw0njx9s6skzdpp3cm2he5s036p07 ✔ amount: 10 continue (y/n): y ✅ txID: sceRdaoqu2AAyLdHCdQkENZaXngGjRoc8nFdGyG8D9pCbTjbk @@ -176,7 +176,7 @@ select chainID: 0 uri: http://127.0.0.1:45778/ext/bc/2mQy8Q9Af9dtZvVM8pKsh2rB3cT3QNLjghpet5Mm5db4N7Hwgk watching for new blocks on 2mQy8Q9Af9dtZvVM8pKsh2rB3cT3QNLjghpet5Mm5db4N7Hwgk 👀 height:1 txs:1 units:440 root:WspVPrHNAwBcJRJPVwt7TW6WT4E74dN8DuD3WXueQTMt5FDdi -✅ sceRdaoqu2AAyLdHCdQkENZaXngGjRoc8nFdGyG8D9pCbTjbk actor: morpheus1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsp30ucp units: 440 summary (*actions.Transfer): [10.000000000 RED -> morpheus1s3ukd2gnhxl96xa5spzg69w7qd2x4ypve0j5vm0qflvlqr4na5zsezaf2f] +✅ sceRdaoqu2AAyLdHCdQkENZaXngGjRoc8nFdGyG8D9pCbTjbk actor: morpheus1qrzvk4zlwj9zsacqgtufx7zvapd3quufqpxk5rsdd4633m4wz2fdjk97rwu units: 440 summary (*actions.Transfer): [10.000000000 RED -> morpheus1q8rc050907hx39vfejpawjydmwe6uujw0njx9s6skzdpp3cm2he5s036p07] ```
diff --git a/examples/morpheusvm/actions/consts.go b/examples/morpheusvm/actions/consts.go index ee361f1e8a..b1f471b2bd 100644 --- a/examples/morpheusvm/actions/consts.go +++ b/examples/morpheusvm/actions/consts.go @@ -3,11 +3,4 @@ package actions -// Note: Registry will error during initialization if a duplicate ID is assigned. We explicitly assign IDs to avoid accidental remapping. -const ( - transferID uint8 = 0 -) - -const ( - TransferComputeUnits = 1 -) +const TransferComputeUnits = 1 diff --git a/examples/morpheusvm/actions/transfer.go b/examples/morpheusvm/actions/transfer.go index 6959e2cb27..551f0dedc7 100644 --- a/examples/morpheusvm/actions/transfer.go +++ b/examples/morpheusvm/actions/transfer.go @@ -11,8 +11,7 @@ import ( "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/crypto/ed25519" - "github.com/ava-labs/hypersdk/examples/morpheusvm/auth" + mconsts "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" "github.com/ava-labs/hypersdk/examples/morpheusvm/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -22,19 +21,19 @@ var _ chain.Action = (*Transfer)(nil) type Transfer struct { // To is the recipient of the [Value]. - To ed25519.PublicKey `json:"to"` + To codec.Address `json:"to"` // Amount are transferred to [To]. Value uint64 `json:"value"` } func (*Transfer) GetTypeID() uint8 { - return transferID + return mconsts.TransferID } -func (t *Transfer) StateKeys(rauth chain.Auth, _ ids.ID) []string { +func (t *Transfer) StateKeys(auth chain.Auth, _ ids.ID) []string { return []string{ - string(storage.BalanceKey(auth.GetActor(rauth))), + string(storage.BalanceKey(auth.Actor())), string(storage.BalanceKey(t.To)), } } @@ -52,15 +51,14 @@ func (t *Transfer) Execute( _ chain.Rules, mu state.Mutable, _ int64, - rauth chain.Auth, + auth chain.Auth, _ ids.ID, _ bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) if t.Value == 0 { return false, 1, OutputValueZero, nil, nil } - if err := storage.SubBalance(ctx, mu, actor, t.Value); err != nil { + if err := storage.SubBalance(ctx, mu, auth.Actor(), t.Value); err != nil { return false, 1, utils.ErrBytes(err), nil, nil } if err := storage.AddBalance(ctx, mu, t.To, t.Value, true); err != nil { @@ -74,19 +72,22 @@ func (*Transfer) MaxComputeUnits(chain.Rules) uint64 { } func (*Transfer) Size() int { - return ed25519.PublicKeyLen + consts.Uint64Len + return codec.AddressLen + consts.Uint64Len } func (t *Transfer) Marshal(p *codec.Packer) { - p.PackPublicKey(t.To) + p.PackAddress(t.To) p.PackUint64(t.Value) } func UnmarshalTransfer(p *codec.Packer, _ *warp.Message) (chain.Action, error) { var transfer Transfer - p.UnpackPublicKey(false, &transfer.To) // can transfer to blackhole + p.UnpackAddress(&transfer.To) // we do not verify the typeID is valid transfer.Value = p.UnpackUint64(true) - return &transfer, p.Err() + if err := p.Err(); err != nil { + return nil, err + } + return &transfer, nil } func (*Transfer) ValidRange(chain.Rules) (int64, int64) { diff --git a/examples/morpheusvm/auth/consts.go b/examples/morpheusvm/auth/consts.go index 67596fbc35..fa377921f1 100644 --- a/examples/morpheusvm/auth/consts.go +++ b/examples/morpheusvm/auth/consts.go @@ -3,15 +3,14 @@ package auth -import "github.com/ava-labs/hypersdk/vm" - -// Note: Registry will error during initialization if a duplicate ID is assigned. We explicitly assign IDs to avoid accidental remapping. -const ( - ed25519ID uint8 = 0 +import ( + "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" + "github.com/ava-labs/hypersdk/vm" ) func Engines() map[uint8]vm.AuthEngine { return map[uint8]vm.AuthEngine{ - ed25519ID: &ED25519AuthEngine{}, + // Only ed25519 batch verification is supported + consts.ED25519ID: &ED25519AuthEngine{}, } } diff --git a/examples/morpheusvm/auth/ed25519.go b/examples/morpheusvm/auth/ed25519.go index ba1455802a..a43ae2d176 100644 --- a/examples/morpheusvm/auth/ed25519.go +++ b/examples/morpheusvm/auth/ed25519.go @@ -12,8 +12,10 @@ import ( "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto" "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" "github.com/ava-labs/hypersdk/examples/morpheusvm/storage" "github.com/ava-labs/hypersdk/state" + "github.com/ava-labs/hypersdk/utils" ) var _ chain.Auth = (*ED25519)(nil) @@ -26,10 +28,19 @@ const ( type ED25519 struct { Signer ed25519.PublicKey `json:"signer"` Signature ed25519.Signature `json:"signature"` + + addr codec.Address +} + +func (d *ED25519) address() codec.Address { + if d.addr == codec.EmptyAddress { + d.addr = NewED25519Address(d.Signer) + } + return d.addr } func (*ED25519) GetTypeID() uint8 { - return ed25519ID + return consts.ED25519ID } func (*ED25519) MaxComputeUnits(chain.Rules) uint64 { @@ -42,7 +53,7 @@ func (*ED25519) ValidRange(chain.Rules) (int64, int64) { func (d *ED25519) StateKeys() []string { return []string{ - string(storage.BalanceKey(d.Signer)), + string(storage.BalanceKey(d.address())), } } @@ -64,8 +75,12 @@ func (d *ED25519) Verify( return d.MaxComputeUnits(r), nil } -func (d *ED25519) Payer() []byte { - return d.Signer[:] +func (d *ED25519) Actor() codec.Address { + return d.address() +} + +func (d *ED25519) Sponsor() codec.Address { + return d.address() } func (*ED25519) Size() int { @@ -73,14 +88,16 @@ func (*ED25519) Size() int { } func (d *ED25519) Marshal(p *codec.Packer) { - p.PackPublicKey(d.Signer) - p.PackSignature(d.Signature) + p.PackFixedBytes(d.Signer[:]) + p.PackFixedBytes(d.Signature[:]) } func UnmarshalED25519(p *codec.Packer, _ *warp.Message) (chain.Auth, error) { var d ED25519 - p.UnpackPublicKey(true, &d.Signer) - p.UnpackSignature(&d.Signature) + signer := d.Signer[:] // avoid allocating additional memory + p.UnpackFixedBytes(ed25519.PublicKeyLen, &signer) + signature := d.Signature[:] // avoid allocating additional memory + p.UnpackFixedBytes(ed25519.SignatureLen, &signature) return &d, p.Err() } @@ -89,7 +106,7 @@ func (d *ED25519) CanDeduct( im state.Immutable, amount uint64, ) error { - bal, err := storage.GetBalance(ctx, im, d.Signer) + bal, err := storage.GetBalance(ctx, im, d.address()) if err != nil { return err } @@ -104,7 +121,7 @@ func (d *ED25519) Deduct( mu state.Mutable, amount uint64, ) error { - return storage.SubBalance(ctx, mu, d.Signer, amount) + return storage.SubBalance(ctx, mu, d.address(), amount) } func (d *ED25519) Refund( @@ -113,7 +130,7 @@ func (d *ED25519) Refund( amount uint64, ) error { // Don't create account if it doesn't exist (may have sent all funds). - return storage.AddBalance(ctx, mu, d.Signer, amount, false) + return storage.AddBalance(ctx, mu, d.address(), amount, false) } var _ chain.AuthFactory = (*ED25519Factory)(nil) @@ -128,7 +145,7 @@ type ED25519Factory struct { func (d *ED25519Factory) Sign(msg []byte, _ chain.Action) (chain.Auth, error) { sig := ed25519.Sign(msg, d.priv) - return &ED25519{d.priv.PublicKey(), sig}, nil + return &ED25519{Signer: d.priv.PublicKey(), Signature: sig}, nil } func (*ED25519Factory) MaxUnits() (uint64, uint64, []uint16) { @@ -146,8 +163,12 @@ func (*ED25519AuthEngine) GetBatchVerifier(cores int, count int) chain.AuthBatch } func (*ED25519AuthEngine) Cache(auth chain.Auth) { - pk := GetSigner(auth) - ed25519.CachePublicKey(pk) + // This should never not happen but we perform this check + // to avoid a panic. + pauth, ok := auth.(*ED25519) + if ok { + ed25519.CachePublicKey(pauth.Signer) + } } type ED25519Batch struct { @@ -185,3 +206,7 @@ func (b *ED25519Batch) Done() []func() error { } return []func() error{b.batch.VerifyAsync()} } + +func NewED25519Address(pk ed25519.PublicKey) codec.Address { + return codec.CreateAddress(consts.ED25519ID, utils.ToID(pk[:])) +} diff --git a/examples/morpheusvm/auth/helpers.go b/examples/morpheusvm/auth/helpers.go deleted file mode 100644 index 340f44f367..0000000000 --- a/examples/morpheusvm/auth/helpers.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package auth - -import ( - "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/crypto/ed25519" -) - -func GetActor(auth chain.Auth) ed25519.PublicKey { - switch a := auth.(type) { - case *ED25519: - return a.Signer - default: - return ed25519.EmptyPublicKey - } -} - -func GetSigner(auth chain.Auth) ed25519.PublicKey { - switch a := auth.(type) { - case *ED25519: - return a.Signer - default: - return ed25519.EmptyPublicKey - } -} diff --git a/examples/morpheusvm/auth/secp256r1.go b/examples/morpheusvm/auth/secp256r1.go new file mode 100644 index 0000000000..3dffad22ec --- /dev/null +++ b/examples/morpheusvm/auth/secp256r1.go @@ -0,0 +1,159 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +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" + "github.com/ava-labs/hypersdk/crypto/secp256r1" + "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" + "github.com/ava-labs/hypersdk/examples/morpheusvm/storage" + "github.com/ava-labs/hypersdk/state" + "github.com/ava-labs/hypersdk/utils" +) + +var _ chain.Auth = (*SECP256R1)(nil) + +const ( + SECP256R1ComputeUnits = 10 // can't be batched like ed25519 + SECP256R1Size = secp256r1.PublicKeyLen + secp256r1.SignatureLen +) + +type SECP256R1 struct { + Signer secp256r1.PublicKey `json:"signer"` + Signature secp256r1.Signature `json:"signature"` + + addr codec.Address +} + +func (d *SECP256R1) address() codec.Address { + if d.addr == codec.EmptyAddress { + d.addr = NewSECP256R1Address(d.Signer) + } + return d.addr +} + +func (*SECP256R1) GetTypeID() uint8 { + return consts.SECP256R1ID +} + +func (*SECP256R1) MaxComputeUnits(chain.Rules) uint64 { + return SECP256R1ComputeUnits +} + +func (*SECP256R1) ValidRange(chain.Rules) (int64, int64) { + return -1, -1 +} + +func (d *SECP256R1) StateKeys() []string { + return []string{ + string(storage.BalanceKey(d.address())), + } +} + +func (d *SECP256R1) AsyncVerify(msg []byte) error { + if !secp256r1.Verify(msg, d.Signer, d.Signature) { + return crypto.ErrInvalidSignature + } + return nil +} + +func (d *SECP256R1) Verify( + _ context.Context, + r chain.Rules, + _ state.Immutable, + _ chain.Action, +) (uint64, error) { + // We don't do anything during verify (there is no additional state to check + // to authorize the signer other than verifying the signature) + return d.MaxComputeUnits(r), nil +} + +func (d *SECP256R1) Actor() codec.Address { + return d.address() +} + +func (d *SECP256R1) Sponsor() codec.Address { + return d.address() +} + +func (*SECP256R1) Size() int { + return SECP256R1Size +} + +func (d *SECP256R1) Marshal(p *codec.Packer) { + p.PackFixedBytes(d.Signer[:]) + p.PackFixedBytes(d.Signature[:]) +} + +func UnmarshalSECP256R1(p *codec.Packer, _ *warp.Message) (chain.Auth, error) { + var d SECP256R1 + signer := d.Signer[:] // avoid allocating additional memory + p.UnpackFixedBytes(secp256r1.PublicKeyLen, &signer) + signature := d.Signature[:] // avoid allocating additional memory + p.UnpackFixedBytes(secp256r1.SignatureLen, &signature) + return &d, p.Err() +} + +func (d *SECP256R1) CanDeduct( + ctx context.Context, + im state.Immutable, + amount uint64, +) error { + bal, err := storage.GetBalance(ctx, im, d.address()) + if err != nil { + return err + } + if bal < amount { + return storage.ErrInvalidBalance + } + return nil +} + +func (d *SECP256R1) Deduct( + ctx context.Context, + mu state.Mutable, + amount uint64, +) error { + return storage.SubBalance(ctx, mu, d.address(), amount) +} + +func (d *SECP256R1) Refund( + ctx context.Context, + mu state.Mutable, + amount uint64, +) error { + // Don't create account if it doesn't exist (may have sent all funds). + return storage.AddBalance(ctx, mu, d.address(), amount, false) +} + +var _ chain.AuthFactory = (*SECP256R1Factory)(nil) + +type SECP256R1Factory struct { + priv secp256r1.PrivateKey +} + +func NewSECP256R1Factory(priv secp256r1.PrivateKey) *SECP256R1Factory { + return &SECP256R1Factory{priv} +} + +func (d *SECP256R1Factory) Sign(msg []byte, _ chain.Action) (chain.Auth, error) { + sig, err := secp256r1.Sign(msg, d.priv) + if err != nil { + return nil, err + } + return &SECP256R1{Signer: d.priv.PublicKey(), Signature: sig}, nil +} + +func (*SECP256R1Factory) MaxUnits() (uint64, uint64, []uint16) { + return SECP256R1Size, SECP256R1ComputeUnits, []uint16{storage.BalanceChunks} +} + +func NewSECP256R1Address(pk secp256r1.PublicKey) codec.Address { + return codec.CreateAddress(consts.SECP256R1ID, utils.ToID(pk[:])) +} diff --git a/examples/morpheusvm/cmd/morpheus-cli/cmd/action.go b/examples/morpheusvm/cmd/morpheus-cli/cmd/action.go index cd85820a10..51f6e9a1dd 100644 --- a/examples/morpheusvm/cmd/morpheus-cli/cmd/action.go +++ b/examples/morpheusvm/cmd/morpheus-cli/cmd/action.go @@ -22,13 +22,13 @@ var transferCmd = &cobra.Command{ Use: "transfer", RunE: func(*cobra.Command, []string) error { ctx := context.Background() - _, priv, factory, cli, bcli, err := handler.DefaultActor() + _, priv, factory, cli, bcli, ws, err := handler.DefaultActor() if err != nil { return err } // Get balance info - balance, err := handler.GetBalance(ctx, bcli, priv.PublicKey()) + balance, err := handler.GetBalance(ctx, bcli, priv.Address) if balance == 0 || err != nil { return err } @@ -55,7 +55,7 @@ var transferCmd = &cobra.Command{ _, _, err = sendAndWait(ctx, nil, &actions.Transfer{ To: recipient, Value: amount, - }, cli, bcli, factory, true) + }, cli, bcli, ws, factory, true) return err }, } diff --git a/examples/morpheusvm/cmd/morpheus-cli/cmd/errors.go b/examples/morpheusvm/cmd/morpheus-cli/cmd/errors.go index c408d40798..600c93481c 100644 --- a/examples/morpheusvm/cmd/morpheus-cli/cmd/errors.go +++ b/examples/morpheusvm/cmd/morpheus-cli/cmd/errors.go @@ -8,4 +8,6 @@ import "errors" var ( ErrInvalidArgs = errors.New("invalid args") ErrMissingSubcommand = errors.New("must specify a subcommand") + ErrInvalidAddress = errors.New("invalid address") + ErrInvalidKeyType = errors.New("invalid key type") ) diff --git a/examples/morpheusvm/cmd/morpheus-cli/cmd/handler.go b/examples/morpheusvm/cmd/morpheus-cli/cmd/handler.go index a9ad95d70d..9985696654 100644 --- a/examples/morpheusvm/cmd/morpheus-cli/cmd/handler.go +++ b/examples/morpheusvm/cmd/morpheus-cli/cmd/handler.go @@ -7,14 +7,17 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/cli" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/crypto/secp256r1" "github.com/ava-labs/hypersdk/examples/morpheusvm/auth" "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" brpc "github.com/ava-labs/hypersdk/examples/morpheusvm/rpc" - "github.com/ava-labs/hypersdk/examples/morpheusvm/utils" + "github.com/ava-labs/hypersdk/pubsub" "github.com/ava-labs/hypersdk/rpc" - hutils "github.com/ava-labs/hypersdk/utils" + "github.com/ava-labs/hypersdk/utils" ) var _ cli.Controller = (*Controller)(nil) @@ -32,52 +35,69 @@ func (h *Handler) Root() *cli.Handler { } func (h *Handler) DefaultActor() ( - ids.ID, ed25519.PrivateKey, *auth.ED25519Factory, - *rpc.JSONRPCClient, *brpc.JSONRPCClient, error, + ids.ID, *cli.PrivateKey, chain.AuthFactory, + *rpc.JSONRPCClient, *brpc.JSONRPCClient, *rpc.WebSocketClient, error, ) { - priv, err := h.h.GetDefaultKey(true) + addr, priv, err := h.h.GetDefaultKey(true) if err != nil { - return ids.Empty, ed25519.EmptyPrivateKey, nil, nil, nil, err + return ids.Empty, nil, nil, nil, nil, nil, err + } + var factory chain.AuthFactory + switch addr[0] { + case consts.ED25519ID: + factory = auth.NewED25519Factory(ed25519.PrivateKey(priv)) + case consts.SECP256R1ID: + factory = auth.NewSECP256R1Factory(secp256r1.PrivateKey(priv)) + default: + return ids.Empty, nil, nil, nil, nil, nil, ErrInvalidAddress } chainID, uris, err := h.h.GetDefaultChain(true) if err != nil { - return ids.Empty, ed25519.EmptyPrivateKey, nil, nil, nil, err + return ids.Empty, nil, nil, nil, nil, nil, err + } + jcli := rpc.NewJSONRPCClient(uris[0]) + networkID, _, _, err := jcli.Network(context.TODO()) + if err != nil { + return ids.Empty, nil, nil, nil, nil, nil, err } - cli := rpc.NewJSONRPCClient(uris[0]) - networkID, _, _, err := cli.Network(context.TODO()) + ws, err := rpc.NewWebSocketClient(uris[0], rpc.DefaultHandshakeTimeout, pubsub.MaxPendingMessages, pubsub.MaxReadMessageSize) if err != nil { - return ids.Empty, ed25519.EmptyPrivateKey, nil, nil, nil, err + return ids.Empty, nil, nil, nil, nil, nil, err } // For [defaultActor], we always send requests to the first returned URI. - return chainID, priv, auth.NewED25519Factory( - priv, - ), cli, + return chainID, &cli.PrivateKey{ + Address: addr, + Bytes: priv, + }, factory, jcli, brpc.NewJSONRPCClient( uris[0], networkID, chainID, - ), nil + ), ws, nil } func (*Handler) GetBalance( ctx context.Context, cli *brpc.JSONRPCClient, - publicKey ed25519.PublicKey, + addr codec.Address, ) (uint64, error) { - addr := utils.Address(publicKey) - balance, err := cli.Balance(ctx, addr) + saddr, err := codec.AddressBech32(consts.HRP, addr) + if err != nil { + return 0, err + } + balance, err := cli.Balance(ctx, saddr) if err != nil { return 0, err } if balance == 0 { - hutils.Outf("{{red}}balance:{{/}} 0 %s\n", consts.Symbol) - hutils.Outf("{{red}}please send funds to %s{{/}}\n", addr) - hutils.Outf("{{red}}exiting...{{/}}\n") + utils.Outf("{{red}}balance:{{/}} 0 %s\n", consts.Symbol) + utils.Outf("{{red}}please send funds to %s{{/}}\n", saddr) + utils.Outf("{{red}}exiting...{{/}}\n") return 0, nil } - hutils.Outf( + utils.Outf( "{{yellow}}balance:{{/}} %s %s\n", - hutils.FormatBalance(balance, consts.Decimals), + utils.FormatBalance(balance, consts.Decimals), consts.Symbol, ) return balance, nil @@ -103,10 +123,10 @@ func (*Controller) Decimals() uint8 { return consts.Decimals } -func (*Controller) Address(pk ed25519.PublicKey) string { - return utils.Address(pk) +func (*Controller) Address(addr codec.Address) string { + return codec.MustAddressBech32(consts.HRP, addr) } -func (*Controller) ParseAddress(address string) (ed25519.PublicKey, error) { - return utils.ParseAddress(address) +func (*Controller) ParseAddress(addr string) (codec.Address, error) { + return codec.ParseAddressBech32(consts.HRP, addr) } diff --git a/examples/morpheusvm/cmd/morpheus-cli/cmd/key.go b/examples/morpheusvm/cmd/morpheus-cli/cmd/key.go index 5ebe54dc59..61d436c69f 100644 --- a/examples/morpheusvm/cmd/morpheus-cli/cmd/key.go +++ b/examples/morpheusvm/cmd/morpheus-cli/cmd/key.go @@ -5,15 +5,95 @@ package cmd import ( "context" + "fmt" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/hypersdk/cli" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/crypto/secp256r1" + "github.com/ava-labs/hypersdk/examples/morpheusvm/auth" "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" brpc "github.com/ava-labs/hypersdk/examples/morpheusvm/rpc" "github.com/ava-labs/hypersdk/utils" "github.com/spf13/cobra" ) +const ( + ed25519Key = "ed25519" + secp256r1Key = "secp256r1" +) + +func checkKeyType(k string) error { + if k != ed25519Key && k != secp256r1Key { + return fmt.Errorf("%w: %s", ErrInvalidKeyType, k) + } + return nil +} + +func getKeyType(addr codec.Address) (string, error) { + switch addr[0] { + case consts.ED25519ID: + return ed25519Key, nil + case consts.SECP256R1ID: + return secp256r1Key, nil + default: + return "", ErrInvalidKeyType + } +} + +func generatePrivateKey(k string) (*cli.PrivateKey, error) { + switch k { + case ed25519Key: + p, err := ed25519.GeneratePrivateKey() + if err != nil { + return nil, err + } + return &cli.PrivateKey{ + Address: auth.NewED25519Address(p.PublicKey()), + Bytes: p[:], + }, nil + case secp256r1Key: + p, err := secp256r1.GeneratePrivateKey() + if err != nil { + return nil, err + } + return &cli.PrivateKey{ + Address: auth.NewSECP256R1Address(p.PublicKey()), + Bytes: p[:], + }, nil + default: + return nil, ErrInvalidKeyType + } +} + +func loadPrivateKey(k string, path string) (*cli.PrivateKey, error) { + switch k { + case ed25519Key: + p, err := utils.LoadBytes(path, ed25519.PrivateKeyLen) + if err != nil { + return nil, err + } + pk := ed25519.PrivateKey(p) + return &cli.PrivateKey{ + Address: auth.NewED25519Address(pk.PublicKey()), + Bytes: p, + }, nil + case secp256r1Key: + p, err := utils.LoadBytes(path, secp256r1.PrivateKeyLen) + if err != nil { + return nil, err + } + pk := secp256r1.PrivateKey(p) + return &cli.PrivateKey{ + Address: auth.NewSECP256R1Address(pk.PublicKey()), + Bytes: p, + }, nil + default: + return nil, ErrInvalidKeyType + } +} + var keyCmd = &cobra.Command{ Use: "key", RunE: func(*cobra.Command, []string) error { @@ -22,22 +102,56 @@ var keyCmd = &cobra.Command{ } var genKeyCmd = &cobra.Command{ - Use: "generate", - RunE: func(*cobra.Command, []string) error { - return handler.Root().GenerateKey() + Use: "generate [ed25519/secp256r1]", + PreRunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return ErrInvalidArgs + } + return checkKeyType(args[0]) + }, + RunE: func(_ *cobra.Command, args []string) error { + priv, err := generatePrivateKey(args[0]) + if err != nil { + return err + } + if err := handler.h.StoreKey(priv); err != nil { + return err + } + if err := handler.h.StoreDefaultKey(priv.Address); err != nil { + return err + } + utils.Outf( + "{{green}}created address:{{/}} %s", + codec.MustAddressBech32(consts.HRP, priv.Address), + ) + return nil }, } var importKeyCmd = &cobra.Command{ - Use: "import [path]", + Use: "import [type] [path]", PreRunE: func(cmd *cobra.Command, args []string) error { - if len(args) != 1 { + if len(args) != 2 { return ErrInvalidArgs } - return nil + return checkKeyType(args[0]) }, RunE: func(_ *cobra.Command, args []string) error { - return handler.Root().ImportKey(args[0]) + priv, err := loadPrivateKey(args[0], args[1]) + if err != nil { + return err + } + if err := handler.h.StoreKey(priv); err != nil { + return err + } + if err := handler.h.StoreDefaultKey(priv.Address); err != nil { + return err + } + utils.Outf( + "{{green}}imported address:{{/}} %s", + codec.MustAddressBech32(consts.HRP, priv.Address), + ) + return nil }, } @@ -48,9 +162,18 @@ func lookupSetKeyBalance(choice int, address string, uri string, networkID uint3 if err != nil { return err } + addr, err := codec.ParseAddressBech32(consts.HRP, address) + if err != nil { + return err + } + keyType, err := getKeyType(addr) + if err != nil { + return err + } utils.Outf( - "%d) {{cyan}}address:{{/}} %s {{cyan}}balance:{{/}} %s %s\n", + "%d) {{cyan}}address (%s):{{/}} %s {{cyan}}balance:{{/}} %s %s\n", choice, + keyType, address, utils.FormatBalance(balance, consts.Decimals), consts.Symbol, @@ -65,8 +188,8 @@ var setKeyCmd = &cobra.Command{ }, } -func lookupKeyBalance(pk ed25519.PublicKey, uri string, networkID uint32, chainID ids.ID, _ ids.ID) error { - _, err := handler.GetBalance(context.TODO(), brpc.NewJSONRPCClient(uri, networkID, chainID), pk) +func lookupKeyBalance(addr codec.Address, uri string, networkID uint32, chainID ids.ID, _ ids.ID) error { + _, err := handler.GetBalance(context.TODO(), brpc.NewJSONRPCClient(uri, networkID, chainID), addr) return err } diff --git a/examples/morpheusvm/cmd/morpheus-cli/cmd/resolutions.go b/examples/morpheusvm/cmd/morpheus-cli/cmd/resolutions.go index d486678461..c64ef4dbb4 100644 --- a/examples/morpheusvm/cmd/morpheus-cli/cmd/resolutions.go +++ b/examples/morpheusvm/cmd/morpheus-cli/cmd/resolutions.go @@ -12,57 +12,67 @@ import ( "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" "github.com/ava-labs/hypersdk/examples/morpheusvm/actions" - "github.com/ava-labs/hypersdk/examples/morpheusvm/auth" "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" brpc "github.com/ava-labs/hypersdk/examples/morpheusvm/rpc" - tutils "github.com/ava-labs/hypersdk/examples/morpheusvm/utils" "github.com/ava-labs/hypersdk/rpc" "github.com/ava-labs/hypersdk/utils" ) -// TODO: use websockets +// sendAndWait may not be used concurrently func sendAndWait( ctx context.Context, warpMsg *warp.Message, action chain.Action, cli *rpc.JSONRPCClient, - bcli *brpc.JSONRPCClient, factory chain.AuthFactory, printStatus bool, + 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 } - submit, tx, _, err := cli.GenerateTransaction(ctx, parser, warpMsg, action, factory) + _, tx, _, err := cli.GenerateTransaction(ctx, parser, warpMsg, action, factory) if err != nil { return false, ids.Empty, err } - if err := submit(ctx); err != nil { + if err := ws.RegisterTx(tx); err != nil { return false, ids.Empty, err } - success, _, err := bcli.WaitForTransaction(ctx, tx.ID()) - if err != nil { - return false, ids.Empty, err + var result *chain.Result + for { + txID, txErr, txResult, err := ws.ListenTx(ctx) + if err != nil { + return false, ids.Empty, err + } + if txErr != nil { + return false, ids.Empty, txErr + } + if txID == tx.ID() { + result = txResult + break + } + utils.Outf("{{yellow}}skipping unexpected transaction:{{/}} %s\n", tx.ID()) } if printStatus { - handler.Root().PrintStatus(tx.ID(), success) + handler.Root().PrintStatus(tx.ID(), result.Success) } - return success, tx.ID(), nil + return result.Success, tx.ID(), nil } func handleTx(tx *chain.Transaction, result *chain.Result) { summaryStr := string(result.Output) - actor := auth.GetActor(tx.Auth) + actor := tx.Auth.Actor() status := "⚠️" if result.Success { status = "✅" switch action := tx.Action.(type) { //nolint:gocritic case *actions.Transfer: - summaryStr = fmt.Sprintf("%s %s -> %s", utils.FormatBalance(action.Value, consts.Decimals), consts.Symbol, tutils.Address(action.To)) + summaryStr = fmt.Sprintf("%s %s -> %s", utils.FormatBalance(action.Value, consts.Decimals), consts.Symbol, codec.MustAddressBech32(consts.HRP, action.To)) } } utils.Outf( "%s {{yellow}}%s{{/}} {{yellow}}actor:{{/}} %s {{yellow}}summary (%s):{{/}} [%s] {{yellow}}fee (max %.2f%%):{{/}} %s %s {{yellow}}consumed:{{/}} [%s]\n", status, tx.ID(), - tutils.Address(actor), + codec.MustAddressBech32(consts.HRP, actor), reflect.TypeOf(tx.Action), summaryStr, float64(result.Fee)/float64(tx.Base.MaxFee)*100, diff --git a/examples/morpheusvm/cmd/morpheus-cli/cmd/spam.go b/examples/morpheusvm/cmd/morpheus-cli/cmd/spam.go index bf00e68986..b6eca45e67 100644 --- a/examples/morpheusvm/cmd/morpheus-cli/cmd/spam.go +++ b/examples/morpheusvm/cmd/morpheus-cli/cmd/spam.go @@ -8,16 +8,31 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/hypersdk/chain" + "github.com/ava-labs/hypersdk/cli" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/crypto/secp256r1" "github.com/ava-labs/hypersdk/examples/morpheusvm/actions" "github.com/ava-labs/hypersdk/examples/morpheusvm/auth" "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" brpc "github.com/ava-labs/hypersdk/examples/morpheusvm/rpc" + "github.com/ava-labs/hypersdk/pubsub" "github.com/ava-labs/hypersdk/rpc" "github.com/ava-labs/hypersdk/utils" "github.com/spf13/cobra" ) +func getFactory(priv *cli.PrivateKey) (chain.AuthFactory, error) { + switch priv.Address[0] { + case consts.ED25519ID: + return auth.NewED25519Factory(ed25519.PrivateKey(priv.Bytes)), nil + case consts.SECP256R1ID: + return auth.NewSECP256R1Factory(secp256r1.PrivateKey(priv.Bytes)), nil + default: + return nil, ErrInvalidKeyType + } +} + var spamCmd = &cobra.Command{ Use: "spam", RunE: func(*cobra.Command, []string) error { @@ -26,22 +41,36 @@ var spamCmd = &cobra.Command{ } var runSpamCmd = &cobra.Command{ - Use: "run", - RunE: func(*cobra.Command, []string) error { + Use: "run [ed25519/secp256r1]", + PreRunE: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return ErrInvalidArgs + } + return checkKeyType(args[0]) + }, + RunE: func(_ *cobra.Command, args []string) error { var bclient *brpc.JSONRPCClient + var wclient *rpc.WebSocketClient var maxFeeParsed *uint64 if maxFee >= 0 { v := uint64(maxFee) maxFeeParsed = &v } return handler.Root().Spam(maxTxBacklog, maxFeeParsed, randomRecipient, - func(uri string, networkID uint32, chainID ids.ID) { + func(uri string, networkID uint32, chainID ids.ID) error { // createClient bclient = brpc.NewJSONRPCClient(uri, networkID, chainID) + ws, err := rpc.NewWebSocketClient(uri, rpc.DefaultHandshakeTimeout, pubsub.MaxPendingMessages, pubsub.MaxReadMessageSize) + if err != nil { + return err + } + wclient = ws + return nil }, - func(pk ed25519.PrivateKey) chain.AuthFactory { - return auth.NewED25519Factory(pk) + getFactory, + func() (*cli.PrivateKey, error) { // createAccount + return generatePrivateKey(args[0]) }, - func(choice int, address string) (uint64, error) { + func(choice int, address string) (uint64, error) { // lookupBalance balance, err := bclient.Balance(context.TODO(), address) if err != nil { return 0, err @@ -55,21 +84,25 @@ var runSpamCmd = &cobra.Command{ ) return balance, err }, - func(ctx context.Context, chainID ids.ID) (chain.Parser, error) { + func(ctx context.Context, chainID ids.ID) (chain.Parser, error) { // getParser return bclient.Parser(ctx) }, - func(pk ed25519.PublicKey, amount uint64) chain.Action { + func(addr codec.Address, amount uint64) chain.Action { // getTransfer return &actions.Transfer{ - To: pk, + To: addr, Value: amount, } }, - func(cli *rpc.JSONRPCClient, pk ed25519.PrivateKey) func(context.Context, uint64) error { + 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{ - To: pk.PublicKey(), + factory, err := getFactory(priv) + if err != nil { + return err + } + _, _, err = sendAndWait(ictx, nil, &actions.Transfer{ + To: priv.Address, Value: count, // prevent duplicate txs - }, cli, bclient, auth.NewED25519Factory(pk), false) + }, cli, bclient, wclient, factory, false) return err } }, diff --git a/examples/morpheusvm/config/config.go b/examples/morpheusvm/config/config.go index 7cbdac3abf..e8e307990a 100644 --- a/examples/morpheusvm/config/config.go +++ b/examples/morpheusvm/config/config.go @@ -12,12 +12,12 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/profiler" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/config" "github.com/ava-labs/hypersdk/trace" "github.com/ava-labs/hypersdk/vm" "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" - "github.com/ava-labs/hypersdk/examples/morpheusvm/utils" "github.com/ava-labs/hypersdk/examples/morpheusvm/version" ) @@ -48,9 +48,9 @@ type Config struct { StreamingBacklogSize int `json:"streamingBacklogSize"` // Mempool - MempoolSize int `json:"mempoolSize"` - MempoolPayerSize int `json:"mempoolPayerSize"` - MempoolExemptPayers []string `json:"mempoolExemptPayers"` + MempoolSize int `json:"mempoolSize"` + MempoolSponsorSize int `json:"mempoolSponsorSize"` + MempoolExemptSponsors []string `json:"mempoolExemptSponsors"` // Misc VerifySignatures bool `json:"verifySignatures"` @@ -61,9 +61,9 @@ type Config struct { // State Sync StateSyncServerDelay time.Duration `json:"stateSyncServerDelay"` // for testing - loaded bool - nodeID ids.NodeID - parsedExemptPayers [][]byte + loaded bool + nodeID ids.NodeID + parsedExemptSponsors []codec.Address } func New(nodeID ids.NodeID, b []byte) (*Config, error) { @@ -76,15 +76,15 @@ func New(nodeID ids.NodeID, b []byte) (*Config, error) { c.loaded = true } - // Parse any exempt payers (usually used when a single account is + // Parse any exempt sponsors (usually used when a single account is // broadcasting many txs at once) - c.parsedExemptPayers = make([][]byte, len(c.MempoolExemptPayers)) - for i, payer := range c.MempoolExemptPayers { - p, err := utils.ParseAddress(payer) + c.parsedExemptSponsors = make([]codec.Address, len(c.MempoolExemptSponsors)) + for i, sponsor := range c.MempoolExemptSponsors { + p, err := codec.ParseAddressBech32(consts.HRP, sponsor) if err != nil { return nil, err } - c.parsedExemptPayers[i] = p[:] + c.parsedExemptSponsors[i] = p } return c, nil } @@ -95,21 +95,21 @@ func (c *Config) setDefault() { c.RootGenerationCores = c.Config.GetRootGenerationCores() c.TransactionExecutionCores = c.Config.GetTransactionExecutionCores() c.MempoolSize = c.Config.GetMempoolSize() - c.MempoolPayerSize = c.Config.GetMempoolPayerSize() + c.MempoolSponsorSize = c.Config.GetMempoolSponsorSize() c.StateSyncServerDelay = c.Config.GetStateSyncServerDelay() c.StreamingBacklogSize = c.Config.GetStreamingBacklogSize() c.VerifySignatures = c.Config.GetVerifySignatures() c.StoreTransactions = defaultStoreTransactions } -func (c *Config) GetLogLevel() logging.Level { return c.LogLevel } -func (c *Config) GetTestMode() bool { return c.TestMode } -func (c *Config) GetSignatureVerificationCores() int { return c.SignatureVerificationCores } -func (c *Config) GetRootGenerationCores() int { return c.RootGenerationCores } -func (c *Config) GetTransactionExecutionCores() int { return c.TransactionExecutionCores } -func (c *Config) GetMempoolSize() int { return c.MempoolSize } -func (c *Config) GetMempoolPayerSize() int { return c.MempoolPayerSize } -func (c *Config) GetMempoolExemptPayers() [][]byte { return c.parsedExemptPayers } +func (c *Config) GetLogLevel() logging.Level { return c.LogLevel } +func (c *Config) GetTestMode() bool { return c.TestMode } +func (c *Config) GetSignatureVerificationCores() int { return c.SignatureVerificationCores } +func (c *Config) GetRootGenerationCores() int { return c.RootGenerationCores } +func (c *Config) GetTransactionExecutionCores() int { return c.TransactionExecutionCores } +func (c *Config) GetMempoolSize() int { return c.MempoolSize } +func (c *Config) GetMempoolSponsorSize() int { return c.MempoolSponsorSize } +func (c *Config) GetMempoolExemptSponsors() []codec.Address { return c.parsedExemptSponsors } func (c *Config) GetTraceConfig() *trace.Config { return &trace.Config{ Enabled: c.TraceEnabled, diff --git a/examples/morpheusvm/consts/types.go b/examples/morpheusvm/consts/types.go new file mode 100644 index 0000000000..2d277d9b74 --- /dev/null +++ b/examples/morpheusvm/consts/types.go @@ -0,0 +1,13 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package consts + +const ( + // Action TypeIDs + TransferID uint8 = 0 + + // Auth TypeIDs + ED25519ID uint8 = 0 + SECP256R1ID uint8 = 1 +) diff --git a/examples/morpheusvm/controller/resolutions.go b/examples/morpheusvm/controller/resolutions.go index f6c837ea68..78c5b89690 100644 --- a/examples/morpheusvm/controller/resolutions.go +++ b/examples/morpheusvm/controller/resolutions.go @@ -10,7 +10,7 @@ import ( "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/examples/morpheusvm/genesis" "github.com/ava-labs/hypersdk/examples/morpheusvm/storage" ) @@ -36,7 +36,7 @@ func (c *Controller) GetTransaction( func (c *Controller) GetBalanceFromState( ctx context.Context, - pk ed25519.PublicKey, + acct codec.Address, ) (uint64, error) { - return storage.GetBalanceFromState(ctx, c.inner.ReadState, pk) + return storage.GetBalanceFromState(ctx, c.inner.ReadState, acct) } diff --git a/examples/morpheusvm/genesis/genesis.go b/examples/morpheusvm/genesis/genesis.go index 91b6a757c3..592372422b 100644 --- a/examples/morpheusvm/genesis/genesis.go +++ b/examples/morpheusvm/genesis/genesis.go @@ -13,10 +13,10 @@ import ( "github.com/ava-labs/avalanchego/x/merkledb" "github.com/ava-labs/hypersdk/chain" + "github.com/ava-labs/hypersdk/codec" hconsts "github.com/ava-labs/hypersdk/consts" "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" "github.com/ava-labs/hypersdk/examples/morpheusvm/storage" - "github.com/ava-labs/hypersdk/examples/morpheusvm/utils" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/vm" ) @@ -29,8 +29,6 @@ type CustomAllocation struct { } type Genesis struct { - HRP string `json:"hrp"` - // State Parameters StateBranchFactor merkledb.BranchFactor `json:"stateBranchFactor"` @@ -69,8 +67,6 @@ type Genesis struct { func Default() *Genesis { return &Genesis{ - HRP: consts.HRP, - // State Parameters StateBranchFactor: merkledb.BranchFactor16, @@ -123,24 +119,21 @@ func (g *Genesis) Load(ctx context.Context, tracer trace.Tracer, mu state.Mutabl ctx, span := tracer.Start(ctx, "Genesis.Load") defer span.End() - if consts.HRP != g.HRP { - return ErrInvalidHRP - } if err := g.StateBranchFactor.Valid(); err != nil { return err } supply := uint64(0) for _, alloc := range g.CustomAllocation { - pk, err := utils.ParseAddress(alloc.Address) + addr, err := codec.ParseAddressBech32(consts.HRP, alloc.Address) if err != nil { - return err + return fmt.Errorf("%w: %s", err, alloc.Address) } supply, err = smath.Add64(supply, alloc.Balance) if err != nil { return err } - if err := storage.SetBalance(ctx, mu, pk, alloc.Balance); err != nil { + if err := storage.SetBalance(ctx, mu, addr, alloc.Balance); err != nil { return fmt.Errorf("%w: addr=%s, bal=%d", err, alloc.Address, alloc.Balance) } } diff --git a/examples/morpheusvm/registry/registry.go b/examples/morpheusvm/registry/registry.go index 1b91d68f68..b09e187cd0 100644 --- a/examples/morpheusvm/registry/registry.go +++ b/examples/morpheusvm/registry/registry.go @@ -26,6 +26,7 @@ func init() { // When registering new auth, ALWAYS make sure to append at the end. consts.AuthRegistry.Register((&auth.ED25519{}).GetTypeID(), auth.UnmarshalED25519, false), + consts.AuthRegistry.Register((&auth.SECP256R1{}).GetTypeID(), auth.UnmarshalSECP256R1, false), ) if errs.Errored() { panic(errs.Err) diff --git a/examples/morpheusvm/rpc/dependencies.go b/examples/morpheusvm/rpc/dependencies.go index 393b29344a..f8e475dc64 100644 --- a/examples/morpheusvm/rpc/dependencies.go +++ b/examples/morpheusvm/rpc/dependencies.go @@ -9,7 +9,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/examples/morpheusvm/genesis" ) @@ -17,5 +17,5 @@ type Controller interface { Genesis() *genesis.Genesis Tracer() trace.Tracer GetTransaction(context.Context, ids.ID) (bool, int64, bool, chain.Dimensions, uint64, error) - GetBalanceFromState(context.Context, ed25519.PublicKey) (uint64, error) + GetBalanceFromState(context.Context, codec.Address) (uint64, error) } diff --git a/examples/morpheusvm/rpc/jsonrpc_server.go b/examples/morpheusvm/rpc/jsonrpc_server.go index 86d71c4eb1..85f8c1919c 100644 --- a/examples/morpheusvm/rpc/jsonrpc_server.go +++ b/examples/morpheusvm/rpc/jsonrpc_server.go @@ -9,8 +9,9 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/hypersdk/chain" + "github.com/ava-labs/hypersdk/codec" + "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" "github.com/ava-labs/hypersdk/examples/morpheusvm/genesis" - "github.com/ava-labs/hypersdk/examples/morpheusvm/utils" ) type JSONRPCServer struct { @@ -71,7 +72,7 @@ func (j *JSONRPCServer) Balance(req *http.Request, args *BalanceArgs, reply *Bal ctx, span := j.c.Tracer().Start(req.Context(), "Server.Balance") defer span.End() - addr, err := utils.ParseAddress(args.Address) + addr, err := codec.ParseAddressBech32(consts.HRP, args.Address) if err != nil { return err } diff --git a/examples/morpheusvm/scripts/run.sh b/examples/morpheusvm/scripts/run.sh index d3cde251a7..bbfda35025 100755 --- a/examples/morpheusvm/scripts/run.sh +++ b/examples/morpheusvm/scripts/run.sh @@ -20,11 +20,13 @@ fi VERSION=v1.10.12 MAX_UINT64=18446744073709551615 MODE=${MODE:-run} +AGO_LOGLEVEL=${AGO_LOGLEVEL:-info} LOGLEVEL=${LOGLEVEL:-info} STATESYNC_DELAY=${STATESYNC_DELAY:-0} MIN_BLOCK_GAP=${MIN_BLOCK_GAP:-100} STORE_TXS=${STORE_TXS:-false} UNLIMITED_USAGE=${UNLIMITED_USAGE:-false} +ADDRESS=${ADDRESS:-morpheus1qrzvk4zlwj9zsacqgtufx7zvapd3quufqpxk5rsdd4633m4wz2fdjk97rwu} if [[ ${MODE} != "run" ]]; then LOGLEVEL=debug STATESYNC_DELAY=100000000 # 100ms @@ -42,6 +44,7 @@ if ${UNLIMITED_USAGE}; then fi echo "Running with:" +echo AGO_LOGLEVEL: ${AGO_LOGLEVEL} echo LOGLEVEL: ${LOGLEVEL} echo VERSION: ${VERSION} echo MODE: ${MODE} @@ -51,6 +54,7 @@ echo MIN_BLOCK_GAP \(ms\): ${MIN_BLOCK_GAP} echo STORE_TXS: ${STORE_TXS} echo WINDOW_TARGET_UNITS: ${WINDOW_TARGET_UNITS} echo MAX_BLOCK_UNITS: ${MAX_BLOCK_UNITS} +echo ADDRESS: ${ADDRESS} ############################ # build avalanchego @@ -115,7 +119,9 @@ find ${TMPDIR}/avalanchego-${VERSION} # Always create allocations (linter doesn't like tab) echo "creating allocations file" cat < ${TMPDIR}/allocations.json -[{"address":"morpheus1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsp30ucp", "balance":10000000000000000000}] +[ + {"address":"${ADDRESS}", "balance":10000000000000000000} +] EOF GENESIS_PATH=$2 @@ -143,8 +149,8 @@ rm -rf ${TMPDIR}/morpheusvm-e2e-profiles cat < ${TMPDIR}/morpheusvm.config { "mempoolSize": 10000000, - "mempoolPayerSize": 10000000, - "mempoolExemptPayers":["morpheus1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsp30ucp"], + "mempoolSponsorSize": 10000000, + "mempoolExemptSponsors":["${ADDRESS}"], "signatureVerificationCores": 2, "rootGenerationCores": 2, "transactionExecutionCores": 2, @@ -193,7 +199,7 @@ ACK_GINKGO_RC=true ginkgo build ./tests/e2e # download avalanche-network-runner # https://github.com/ava-labs/avalanche-network-runner ANR_REPO_PATH=github.com/ava-labs/avalanche-network-runner -ANR_VERSION=v1.7.2 +ANR_VERSION=a641238e9a15a2ce4d9903c266307b93926d3733 # version set go install -v ${ANR_REPO_PATH}@${ANR_VERSION} @@ -212,7 +218,7 @@ killall avalanche-network-runner || true echo "launch avalanche-network-runner in the background" $BIN server \ ---log-level verbo \ +--log-level=verbo \ --port=":12352" \ --grpc-gateway-port=":12353" & PID=${!} @@ -243,7 +249,7 @@ echo "running e2e tests" ./tests/e2e/e2e.test \ --ginkgo.v \ --network-runner-log-level verbo \ ---avalanchego-log-level ${LOGLEVEL} \ +--avalanchego-log-level ${AGO_LOGLEVEL} \ --network-runner-grpc-endpoint="0.0.0.0:12352" \ --network-runner-grpc-gateway-endpoint="0.0.0.0:12353" \ --avalanchego-path=${AVALANCHEGO_PATH} \ diff --git a/examples/morpheusvm/storage/storage.go b/examples/morpheusvm/storage/storage.go index 33d800f897..8d086dd05c 100644 --- a/examples/morpheusvm/storage/storage.go +++ b/examples/morpheusvm/storage/storage.go @@ -8,17 +8,16 @@ import ( "encoding/binary" "errors" "fmt" - "sync" "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" smath "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/hypersdk/chain" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/state" - "github.com/ava-labs/hypersdk/examples/morpheusvm/utils" + mconsts "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" ) type ReadState func(context.Context, [][]byte) ([][]byte, []error) @@ -59,12 +58,6 @@ var ( heightKey = []byte{heightPrefix} timestampKey = []byte{timestampPrefix} feeKey = []byte{feePrefix} - - balanceKeyPool = sync.Pool{ - New: func() any { - return make([]byte, 1+ed25519.PublicKeyLen+consts.Uint16Len) - }, - } ) // [txPrefix] + [txID] @@ -124,11 +117,11 @@ func GetTransaction( } // [balancePrefix] + [address] -func BalanceKey(pk ed25519.PublicKey) (k []byte) { - k = balanceKeyPool.Get().([]byte) +func BalanceKey(addr codec.Address) (k []byte) { + k = make([]byte, 1+codec.AddressLen+consts.Uint16Len) k[0] = balancePrefix - copy(k[1:], pk[:]) - binary.BigEndian.PutUint16(k[1+ed25519.PublicKeyLen:], BalanceChunks) + copy(k[1:], addr[:]) + binary.BigEndian.PutUint16(k[1+codec.AddressLen:], BalanceChunks) return } @@ -136,19 +129,18 @@ func BalanceKey(pk ed25519.PublicKey) (k []byte) { func GetBalance( ctx context.Context, im state.Immutable, - pk ed25519.PublicKey, + addr codec.Address, ) (uint64, error) { - key, bal, _, err := getBalance(ctx, im, pk) - balanceKeyPool.Put(key) + _, bal, _, err := getBalance(ctx, im, addr) return bal, err } func getBalance( ctx context.Context, im state.Immutable, - pk ed25519.PublicKey, + addr codec.Address, ) ([]byte, uint64, bool, error) { - k := BalanceKey(pk) + k := BalanceKey(addr) bal, exists, err := innerGetBalance(im.GetValue(ctx, k)) return k, bal, exists, err } @@ -157,12 +149,11 @@ func getBalance( func GetBalanceFromState( ctx context.Context, f ReadState, - pk ed25519.PublicKey, + addr codec.Address, ) (uint64, error) { - k := BalanceKey(pk) + k := BalanceKey(addr) values, errs := f(ctx, [][]byte{k}) bal, _, err := innerGetBalance(values[0], errs[0]) - balanceKeyPool.Put(k) return bal, err } @@ -182,10 +173,10 @@ func innerGetBalance( func SetBalance( ctx context.Context, mu state.Mutable, - pk ed25519.PublicKey, + addr codec.Address, balance uint64, ) error { - k := BalanceKey(pk) + k := BalanceKey(addr) return setBalance(ctx, mu, k, balance) } @@ -201,11 +192,11 @@ func setBalance( func AddBalance( ctx context.Context, mu state.Mutable, - pk ed25519.PublicKey, + addr codec.Address, amount uint64, create bool, ) error { - key, bal, exists, err := getBalance(ctx, mu, pk) + key, bal, exists, err := getBalance(ctx, mu, addr) if err != nil { return err } @@ -220,7 +211,7 @@ func AddBalance( "%w: could not add balance (bal=%d, addr=%v, amount=%d)", ErrInvalidBalance, bal, - utils.Address(pk), + codec.MustAddressBech32(mconsts.HRP, addr), amount, ) } @@ -230,10 +221,10 @@ func AddBalance( func SubBalance( ctx context.Context, mu state.Mutable, - pk ed25519.PublicKey, + addr codec.Address, amount uint64, ) error { - key, bal, _, err := getBalance(ctx, mu, pk) + key, bal, _, err := getBalance(ctx, mu, addr) if err != nil { return err } @@ -243,7 +234,7 @@ func SubBalance( "%w: could not subtract balance (bal=%d, addr=%v, amount=%d)", ErrInvalidBalance, bal, - utils.Address(pk), + codec.MustAddressBech32(mconsts.HRP, addr), amount, ) } diff --git a/examples/morpheusvm/tests/e2e/e2e_test.go b/examples/morpheusvm/tests/e2e/e2e_test.go index 3cd56f3974..0a0ba27f95 100644 --- a/examples/morpheusvm/tests/e2e/e2e_test.go +++ b/examples/morpheusvm/tests/e2e/e2e_test.go @@ -16,14 +16,14 @@ import ( "github.com/ava-labs/avalanchego/config" "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/examples/morpheusvm/actions" "github.com/ava-labs/hypersdk/examples/morpheusvm/auth" "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" lrpc "github.com/ava-labs/hypersdk/examples/morpheusvm/rpc" - "github.com/ava-labs/hypersdk/examples/morpheusvm/utils" "github.com/ava-labs/hypersdk/rpc" - hutils "github.com/ava-labs/hypersdk/utils" + "github.com/ava-labs/hypersdk/utils" "github.com/fatih/color" ginkgo "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" @@ -192,16 +192,18 @@ var _ = ginkgo.BeforeSuite(func() { gomega.Expect(err).Should(gomega.BeNil()) // Load default pk - priv, err = ed25519.HexToKey( + privBytes, err := codec.LoadHex( "323b1d8f4eed5f0da9da93071b034f2dce9d2d22692c172f3cb252a64ddfafd01b057de320297c29ad0c1f589ea216869cf1938d88c9fbd70d6748323dbf2fa7", //nolint:lll + ed25519.PrivateKeyLen, ) gomega.Ω(err).Should(gomega.BeNil()) + priv = ed25519.PrivateKey(privBytes) factory = auth.NewED25519Factory(priv) - rsender = priv.PublicKey() - sender = utils.Address(rsender) - hutils.Outf("\n{{yellow}}$ loaded address:{{/}} %s\n\n", sender) + rsender = auth.NewED25519Address(priv.PublicKey()) + sender = codec.MustAddressBech32(consts.HRP, rsender) + utils.Outf("\n{{yellow}}$ loaded address:{{/}} %s\n\n", sender) - hutils.Outf( + utils.Outf( "{{green}}sending 'start' with binary path:{{/}} %q (%q)\n", execPath, consts.ID, @@ -253,7 +255,7 @@ var _ = ginkgo.BeforeSuite(func() { ) cancel() gomega.Expect(err).Should(gomega.BeNil()) - hutils.Outf( + utils.Outf( "{{green}}successfully started cluster:{{/}} %s {{green}}subnets:{{/}} %+v\n", resp.ClusterInfo.RootDataDir, resp.GetClusterInfo().GetSubnets(), @@ -289,7 +291,7 @@ var _ = ginkgo.BeforeSuite(func() { blockchainID = sresp.ChainIds[0] subnetID := sresp.ClusterInfo.CustomChains[blockchainID].SubnetId - hutils.Outf( + utils.Outf( "{{green}}successfully added chain:{{/}} %s {{green}}subnet:{{/}} %s {{green}}participants:{{/}} %+v\n", blockchainID, subnetID, @@ -344,7 +346,7 @@ var _ = ginkgo.BeforeSuite(func() { var ( priv ed25519.PrivateKey factory *auth.ED25519Factory - rsender ed25519.PublicKey + rsender codec.Address sender string instances []instance @@ -360,17 +362,17 @@ type instance struct { var _ = ginkgo.AfterSuite(func() { switch mode { case modeTest, modeFullTest: - hutils.Outf("{{red}}shutting down cluster{{/}}\n") + utils.Outf("{{red}}shutting down cluster{{/}}\n") ctx, cancel := context.WithTimeout(context.Background(), 2*time.Minute) _, err := anrCli.Stop(ctx) cancel() gomega.Expect(err).Should(gomega.BeNil()) case modeRun: - hutils.Outf("{{yellow}}skipping cluster shutdown{{/}}\n\n") - hutils.Outf("{{cyan}}Blockchain:{{/}} %s\n", blockchainID) + utils.Outf("{{yellow}}skipping cluster shutdown{{/}}\n\n") + utils.Outf("{{cyan}}Blockchain:{{/}} %s\n", blockchainID) for _, member := range instances { - hutils.Outf("%s URI: %s\n", member.nodeID, member.uri) + utils.Outf("%s URI: %s\n", member.nodeID, member.uri) } } gomega.Expect(anrCli.Close()).Should(gomega.BeNil()) @@ -402,7 +404,7 @@ var _ = ginkgo.Describe("[Network]", func() { var _ = ginkgo.Describe("[Test]", func() { switch mode { case modeRun: - hutils.Outf("{{yellow}}skipping tests{{/}}\n") + utils.Outf("{{yellow}}skipping tests{{/}}\n") return } @@ -413,7 +415,8 @@ var _ = ginkgo.Describe("[Test]", func() { other, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) - aother := utils.Address(other.PublicKey()) + aother := auth.NewED25519Address(other.PublicKey()) + aotherStr := codec.MustAddressBech32(consts.HRP, aother) ginkgo.By("issue Transfer to the first node", func() { // Generate transaction @@ -424,28 +427,28 @@ var _ = ginkgo.Describe("[Test]", func() { parser, nil, &actions.Transfer{ - To: other.PublicKey(), + To: aother, Value: sendAmount, }, factory, ) gomega.Ω(err).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}generated transaction{{/}}\n") + utils.Outf("{{yellow}}generated transaction{{/}}\n") // Broadcast and wait for transaction gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") + utils.Outf("{{yellow}}submitted transaction{{/}}\n") ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) success, fee, err := instances[0].lcli.WaitForTransaction(ctx, tx.ID()) cancel() gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found transaction{{/}}\n") + utils.Outf("{{yellow}}found transaction{{/}}\n") // Check sender balance balance, err := instances[0].lcli.Balance(context.Background(), sender) gomega.Ω(err).Should(gomega.BeNil()) - hutils.Outf( + utils.Outf( "{{yellow}}start=%d fee=%d send=%d balance=%d{{/}}\n", startAmount, fee, @@ -453,7 +456,7 @@ var _ = ginkgo.Describe("[Test]", func() { balance, ) gomega.Ω(balance).Should(gomega.Equal(startAmount - fee - sendAmount)) - hutils.Outf("{{yellow}}fetched balance{{/}}\n") + utils.Outf("{{yellow}}fetched balance{{/}}\n") }) ginkgo.By("check if Transfer has been accepted from all nodes", func() { @@ -471,7 +474,7 @@ var _ = ginkgo.Describe("[Test]", func() { } // Check balance of recipient - balance, err := inst.lcli.Balance(context.Background(), aother) + balance, err := inst.lcli.Balance(context.Background(), aotherStr) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(balance).Should(gomega.Equal(sendAmount)) } @@ -480,7 +483,7 @@ var _ = ginkgo.Describe("[Test]", func() { switch mode { case modeTest: - hutils.Outf("{{yellow}}skipping bootstrap and state sync tests{{/}}\n") + utils.Outf("{{yellow}}skipping bootstrap and state sync tests{{/}}\n") return } @@ -510,7 +513,7 @@ var _ = ginkgo.Describe("[Test]", func() { uri := nodeURI + fmt.Sprintf("/ext/bc/%s", blockchainID) bid, err := ids.FromString(blockchainID) gomega.Expect(err).To(gomega.BeNil()) - hutils.Outf("{{blue}}bootstrap node uri: %s{{/}}\n", uri) + utils.Outf("{{blue}}bootstrap node uri: %s{{/}}\n", uri) c := rpc.NewJSONRPCClient(uri) syncClient = c networkID, _, _, err := syncClient.Network(context.TODO()) @@ -544,7 +547,7 @@ var _ = ginkgo.Describe("[Test]", func() { uri := nodeURI + fmt.Sprintf("/ext/bc/%s", blockchainID) bid, err := ids.FromString(blockchainID) gomega.Expect(err).To(gomega.BeNil()) - hutils.Outf("{{blue}}bootstrap node uri: %s{{/}}\n", uri) + utils.Outf("{{blue}}bootstrap node uri: %s{{/}}\n", uri) c := rpc.NewJSONRPCClient(uri) syncClient = c networkID, _, _, err := syncClient.Network(context.TODO()) @@ -585,7 +588,7 @@ var _ = ginkgo.Describe("[Test]", func() { uri := nodeURI + fmt.Sprintf("/ext/bc/%s", blockchainID) bid, err := ids.FromString(blockchainID) gomega.Expect(err).To(gomega.BeNil()) - hutils.Outf("{{blue}}sync node uri: %s{{/}}\n", uri) + utils.Outf("{{blue}}sync node uri: %s{{/}}\n", uri) syncClient = rpc.NewJSONRPCClient(uri) networkID, _, _, err := syncClient.Network(context.TODO()) gomega.Expect(err).To(gomega.BeNil()) @@ -659,7 +662,7 @@ var _ = ginkgo.Describe("[Test]", func() { uri := nodeURI + fmt.Sprintf("/ext/bc/%s", blockchainID) bid, err := ids.FromString(blockchainID) gomega.Expect(err).To(gomega.BeNil()) - hutils.Outf("{{blue}}sync node uri: %s{{/}}\n", uri) + utils.Outf("{{blue}}sync node uri: %s{{/}}\n", uri) syncClient = rpc.NewJSONRPCClient(uri) networkID, _, _, err := syncClient.Network(context.TODO()) gomega.Expect(err).To(gomega.BeNil()) @@ -683,7 +686,7 @@ func awaitHealthy(cli runner_sdk.Client) { if err == nil { return } - hutils.Outf( + utils.Outf( "{{yellow}}waiting for health check to pass:{{/}} %v\n", err, ) @@ -711,12 +714,13 @@ func generateBlocks( // Generate transaction other, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) + aother := auth.NewED25519Address(other.PublicKey()) submit, _, _, err := instances[cumulativeTxs%len(instances)].cli.GenerateTransaction( context.Background(), parser, nil, &actions.Transfer{ - To: other.PublicKey(), + To: aother, Value: 1, }, factory, @@ -724,7 +728,7 @@ func generateBlocks( if failOnError { gomega.Ω(err).Should(gomega.BeNil()) } else if err != nil { - hutils.Outf( + utils.Outf( "{{yellow}}unable to generate transaction:{{/}} %v\n", err, ) @@ -737,7 +741,7 @@ func generateBlocks( if failOnError { gomega.Ω(err).Should(gomega.BeNil()) } else if err != nil { - hutils.Outf( + utils.Outf( "{{yellow}}tx broadcast failed:{{/}} %v\n", err, ) @@ -749,7 +753,7 @@ func generateBlocks( if failOnError { gomega.Ω(err).Should(gomega.BeNil()) } else if err != nil { - hutils.Outf( + utils.Outf( "{{yellow}}height lookup failed:{{/}} %v\n", err, ) @@ -760,7 +764,7 @@ func generateBlocks( break } else if height > lastHeight { lastHeight = height - hutils.Outf("{{yellow}}height=%d count=%d{{/}}\n", height, cumulativeTxs) + utils.Outf("{{yellow}}height=%d count=%d{{/}}\n", height, cumulativeTxs) } // Sleep for a very small amount of time to avoid overloading the @@ -777,6 +781,7 @@ func acceptTransaction(cli *rpc.JSONRPCClient, lcli *lrpc.JSONRPCClient) { // Generate transaction other, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) + aother := auth.NewED25519Address(other.PublicKey()) unitPrices, err := cli.UnitPrices(context.Background(), false) gomega.Ω(err).Should(gomega.BeNil()) submit, tx, maxFee, err := cli.GenerateTransaction( @@ -784,26 +789,26 @@ func acceptTransaction(cli *rpc.JSONRPCClient, lcli *lrpc.JSONRPCClient) { parser, nil, &actions.Transfer{ - To: other.PublicKey(), + To: aother, Value: 1, }, factory, ) gomega.Ω(err).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}generated transaction{{/}} prices: %+v maxFee: %d\n", unitPrices, maxFee) + utils.Outf("{{yellow}}generated transaction{{/}} prices: %+v maxFee: %d\n", unitPrices, maxFee) // Broadcast and wait for transaction gomega.Ω(submit(context.Background())).Should(gomega.BeNil()) - hutils.Outf("{{yellow}}submitted transaction{{/}}\n") + utils.Outf("{{yellow}}submitted transaction{{/}}\n") ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) success, _, err := lcli.WaitForTransaction(ctx, tx.ID()) cancel() if err != nil { - hutils.Outf("{{red}}cannot find transaction: %v{{/}}\n", err) + utils.Outf("{{red}}cannot find transaction: %v{{/}}\n", err) continue } gomega.Ω(success).Should(gomega.BeTrue()) - hutils.Outf("{{yellow}}found transaction{{/}}\n") + utils.Outf("{{yellow}}found transaction{{/}}\n") break } } diff --git a/examples/morpheusvm/tests/integration/integration_test.go b/examples/morpheusvm/tests/integration/integration_test.go index aad6a91e42..d62dcbcf50 100644 --- a/examples/morpheusvm/tests/integration/integration_test.go +++ b/examples/morpheusvm/tests/integration/integration_test.go @@ -36,6 +36,7 @@ import ( "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/crypto/secp256r1" "github.com/ava-labs/hypersdk/pubsub" "github.com/ava-labs/hypersdk/rpc" hutils "github.com/ava-labs/hypersdk/utils" @@ -47,7 +48,6 @@ import ( "github.com/ava-labs/hypersdk/examples/morpheusvm/controller" "github.com/ava-labs/hypersdk/examples/morpheusvm/genesis" lrpc "github.com/ava-labs/hypersdk/examples/morpheusvm/rpc" - "github.com/ava-labs/hypersdk/examples/morpheusvm/utils" ) var ( @@ -93,19 +93,22 @@ func init() { var ( priv ed25519.PrivateKey + pk ed25519.PublicKey factory *auth.ED25519Factory - rsender ed25519.PublicKey - sender string + addr codec.Address + addrStr string priv2 ed25519.PrivateKey + pk2 ed25519.PublicKey factory2 *auth.ED25519Factory - rsender2 ed25519.PublicKey - sender2 string + addr2 codec.Address + addrStr2 string priv3 ed25519.PrivateKey + pk3 ed25519.PublicKey factory3 *auth.ED25519Factory - rsender3 ed25519.PublicKey - sender3 string + addr3 codec.Address + addrStr3 string // when used with embedded VMs genesisBytes []byte @@ -135,34 +138,37 @@ var _ = ginkgo.BeforeSuite(func() { var err error priv, err = ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) + pk = priv.PublicKey() factory = auth.NewED25519Factory(priv) - rsender = priv.PublicKey() - sender = utils.Address(rsender) + addr = auth.NewED25519Address(pk) + addrStr = codec.MustAddressBech32(lconsts.HRP, addr) log.Debug( "generated key", - zap.String("addr", sender), + zap.String("addr", addrStr), zap.String("pk", hex.EncodeToString(priv[:])), ) priv2, err = ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) + pk2 = priv2.PublicKey() factory2 = auth.NewED25519Factory(priv2) - rsender2 = priv2.PublicKey() - sender2 = utils.Address(rsender2) + addr2 = auth.NewED25519Address(pk2) + addrStr2 = codec.MustAddressBech32(lconsts.HRP, addr2) log.Debug( "generated key", - zap.String("addr", sender2), + zap.String("addr", addrStr2), zap.String("pk", hex.EncodeToString(priv2[:])), ) priv3, err = ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) + pk3 = priv3.PublicKey() factory3 = auth.NewED25519Factory(priv3) - rsender3 = priv3.PublicKey() - sender3 = utils.Address(rsender3) + addr3 = auth.NewED25519Address(pk3) + addrStr3 = codec.MustAddressBech32(lconsts.HRP, addr3) log.Debug( "generated key", - zap.String("addr", sender3), + zap.String("addr", addrStr3), zap.String("pk", hex.EncodeToString(priv3[:])), ) @@ -174,7 +180,7 @@ var _ = ginkgo.BeforeSuite(func() { gen.MinBlockGap = 0 gen.CustomAllocation = []*genesis.CustomAllocation{ { - Address: sender, + Address: addrStr, Balance: 10_000_000, }, } @@ -261,6 +267,7 @@ var _ = ginkgo.BeforeSuite(func() { balance, err := cli.Balance(context.Background(), alloc.Address) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(balance).Should(gomega.Equal(alloc.Balance)) + log.Warn("balances", zap.String("addr", alloc.Address), zap.Uint64("bal", balance)) csupply += alloc.Balance } } @@ -315,6 +322,12 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { var transferTxRoot *chain.Transaction ginkgo.It("Gossip TransferTx to a different node", func() { + ginkgo.By("check balance", func() { + balance, err := instances[0].lcli.Balance(context.Background(), addrStr) + gomega.Ω(err).To(gomega.BeNil()) + gomega.Ω(balance).To(gomega.Equal(uint64(10000000))) + }) + ginkgo.By("issue TransferTx", func() { parser, err := instances[0].lcli.Parser(context.Background()) gomega.Ω(err).Should(gomega.BeNil()) @@ -323,7 +336,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: rsender2, + To: addr2, Value: 100_000, // must be more than StateLockup }, factory, @@ -356,7 +369,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { }, nil, &actions.Transfer{ - To: rsender2, + To: addr2, Value: 110, }, ) @@ -421,20 +434,20 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // read: 2 keys reads, 1 had 0 chunks // create: 1 key created // modify: 1 cold key modified - transferTxConsumed := chain.Dimensions{190, 7, 12, 25, 13} + transferTxConsumed := chain.Dimensions{191, 7, 12, 25, 13} 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(247))) + gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(248))) }) ginkgo.By("ensure balance is updated", func() { - balance, err := instances[1].lcli.Balance(context.Background(), sender) + balance, err := instances[1].lcli.Balance(context.Background(), addrStr) gomega.Ω(err).To(gomega.BeNil()) - gomega.Ω(balance).To(gomega.Equal(uint64(9899753))) - balance2, err := instances[1].lcli.Balance(context.Background(), sender2) + gomega.Ω(balance).To(gomega.Equal(uint64(9899752))) + balance2, err := instances[1].lcli.Balance(context.Background(), addrStr2) gomega.Ω(err).To(gomega.BeNil()) gomega.Ω(balance2).To(gomega.Equal(uint64(100000))) }) @@ -449,7 +462,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: rsender2, + To: addr2, Value: 101, }, factory, @@ -468,15 +481,15 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // read: 2 keys reads, 1 chunk each // create: 0 key created // modify: 2 cold key modified - transferTxConsumed := chain.Dimensions{190, 7, 14, 0, 26} + transferTxConsumed := chain.Dimensions{191, 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(237))) + gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(238))) - balance2, err := instances[1].lcli.Balance(context.Background(), sender2) + balance2, err := instances[1].lcli.Balance(context.Background(), addrStr2) gomega.Ω(err).To(gomega.BeNil()) gomega.Ω(balance2).To(gomega.Equal(uint64(100101))) }) @@ -490,7 +503,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: rsender2, + To: addr2, Value: 102, }, factory, @@ -502,7 +515,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: rsender2, + To: addr2, Value: 103, }, factory, @@ -514,7 +527,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: rsender3, + To: addr3, Value: 104, }, factory, @@ -526,7 +539,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: rsender3, + To: addr3, Value: 105, }, factory, @@ -553,12 +566,12 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // create: 0 key created // modify: 2 cold key modified gomega.Ω(results[0].Success).Should(gomega.BeTrue()) - transferTxConsumed := chain.Dimensions{190, 7, 14, 0, 26} + transferTxConsumed := chain.Dimensions{191, 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(237))) + gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(238))) // Unit explanation // @@ -568,12 +581,12 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // create: 0 key created // modify: 2 warm keys modified gomega.Ω(results[1].Success).Should(gomega.BeTrue()) - transferTxConsumed = chain.Dimensions{190, 7, 4, 0, 16} + transferTxConsumed = chain.Dimensions{191, 7, 4, 0, 16} 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(217))) + gomega.Ω(results[1].Fee).Should(gomega.Equal(uint64(218))) // Unit explanation // @@ -583,12 +596,12 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // create: 1 key created (1 chunk) // modify: 1 warm key modified (1 chunk) gomega.Ω(results[2].Success).Should(gomega.BeTrue()) - transferTxConsumed = chain.Dimensions{190, 7, 7, 25, 8} + transferTxConsumed = chain.Dimensions{191, 7, 7, 25, 8} 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(237))) + gomega.Ω(results[2].Fee).Should(gomega.Equal(uint64(238))) // Unit explanation // @@ -598,18 +611,18 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // create: 0 key created // modify: 2 warm keys modified (1 chunk) gomega.Ω(results[3].Success).Should(gomega.BeTrue()) - transferTxConsumed = chain.Dimensions{190, 7, 3, 0, 16} + transferTxConsumed = chain.Dimensions{191, 7, 3, 0, 16} 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(216))) + gomega.Ω(results[3].Fee).Should(gomega.Equal(uint64(217))) // Check end balance - balance2, err := instances[1].lcli.Balance(context.Background(), sender2) + balance2, err := instances[1].lcli.Balance(context.Background(), addrStr2) gomega.Ω(err).To(gomega.BeNil()) gomega.Ω(balance2).To(gomega.Equal(uint64(100306))) - balance3, err := instances[1].lcli.Balance(context.Background(), sender3) + balance3, err := instances[1].lcli.Balance(context.Background(), addrStr3) gomega.Ω(err).To(gomega.BeNil()) gomega.Ω(balance3).To(gomega.Equal(uint64(209))) }) @@ -626,7 +639,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: rsender2, + To: addr2, Value: 200, }, factory, @@ -640,7 +653,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: rsender2, + To: addr2, Value: 201, }, factory, @@ -669,7 +682,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: rsender2, + To: addr2, Value: 203, }, factory, @@ -742,14 +755,14 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { time.Sleep(2 * pubsub.MaxMessageWait) // Fetch balances - balance, err := instances[0].lcli.Balance(context.TODO(), sender) + balance, err := instances[0].lcli.Balance(context.TODO(), addrStr) gomega.Ω(err).Should(gomega.BeNil()) // Send tx other, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) transfer := &actions.Transfer{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Value: 1, } @@ -781,7 +794,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(prices).Should(gomega.Equal(chain.Dimensions{1, 1, 1, 1, 1})) // Check balance modifications are correct - balancea, err := instances[0].lcli.Balance(context.TODO(), sender) + balancea, err := instances[0].lcli.Balance(context.TODO(), addrStr) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(balance).Should(gomega.Equal(balancea + lresults[0].Fee + 1)) @@ -798,7 +811,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { other, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) transfer := &actions.Transfer{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Value: 1, } parser, err := instances[0].lcli.Parser(context.Background()) @@ -841,6 +854,60 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // Close connection when done gomega.Ω(cli.Close()).Should(gomega.BeNil()) }) + + ginkgo.It("sends tokens between ed25519 and secp256r1 addresses", func() { + r1priv, err := secp256r1.GeneratePrivateKey() + gomega.Ω(err).Should(gomega.BeNil()) + r1pk := r1priv.PublicKey() + r1factory := auth.NewSECP256R1Factory(r1priv) + r1addr := auth.NewSECP256R1Address(r1pk) + + ginkgo.By("send to secp256r1", func() { + parser, err := instances[0].lcli.Parser(context.Background()) + gomega.Ω(err).Should(gomega.BeNil()) + submit, _, _, err := instances[0].cli.GenerateTransaction( + context.Background(), + parser, + nil, + &actions.Transfer{ + To: r1addr, + Value: 2000, + }, + 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)) + gomega.Ω(results[0].Success).Should(gomega.BeTrue()) + + balance, err := instances[0].lcli.Balance(context.TODO(), codec.MustAddressBech32(lconsts.HRP, r1addr)) + gomega.Ω(err).Should(gomega.BeNil()) + gomega.Ω(balance).Should(gomega.Equal(uint64(2000))) + }) + + ginkgo.By("send back to ed25519", func() { + parser, err := instances[0].lcli.Parser(context.Background()) + gomega.Ω(err).Should(gomega.BeNil()) + submit, _, _, err := instances[0].cli.GenerateTransaction( + context.Background(), + parser, + nil, + &actions.Transfer{ + To: addr, + Value: 100, + }, + r1factory, + ) + 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)) + gomega.Ω(results[0].Success).Should(gomega.BeTrue()) + }) + }) }) func expectBlk(i instance) func(bool) []*chain.Result { diff --git a/examples/morpheusvm/tests/load/load_test.go b/examples/morpheusvm/tests/load/load_test.go index e0d350606e..1d7d27bbce 100644 --- a/examples/morpheusvm/tests/load/load_test.go +++ b/examples/morpheusvm/tests/load/load_test.go @@ -36,6 +36,7 @@ import ( "go.uber.org/zap" "github.com/ava-labs/hypersdk/chain" + "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/pebble" @@ -49,7 +50,6 @@ import ( "github.com/ava-labs/hypersdk/examples/morpheusvm/controller" "github.com/ava-labs/hypersdk/examples/morpheusvm/genesis" trpc "github.com/ava-labs/hypersdk/examples/morpheusvm/rpc" - "github.com/ava-labs/hypersdk/examples/morpheusvm/utils" "github.com/ava-labs/hypersdk/rpc" ) @@ -89,7 +89,7 @@ type instance struct { type account struct { priv ed25519.PrivateKey factory *auth.ED25519Factory - rsender ed25519.PublicKey + rsender codec.Address sender string } @@ -185,8 +185,8 @@ var _ = ginkgo.BeforeSuite(func() { var err error priv, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) - rsender := priv.PublicKey() - sender := utils.Address(rsender) + rsender := auth.NewED25519Address(priv.PublicKey()) + sender := codec.MustAddressBech32(consts.HRP, rsender) root = &account{priv, auth.NewED25519Factory(priv), rsender, sender} log.Debug( "generated root key", @@ -200,7 +200,8 @@ var _ = ginkgo.BeforeSuite(func() { gen.MinUnitPrice = chain.Dimensions{1, 1, 1, 1, 1} // target must be set less than max, otherwise we will iterate through all txs in mempool gen.WindowTargetUnits = chain.Dimensions{hconsts.NetworkSizeLimit - 10*units.KiB, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64} // disable unit price increase - gen.MaxBlockUnits = chain.Dimensions{hconsts.NetworkSizeLimit, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64} + // leave room for block header + gen.MaxBlockUnits = chain.Dimensions{hconsts.NetworkSizeLimit - units.KiB, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64} gen.MinBlockGap = 0 // don't require time between blocks gen.ValidityWindow = 1_000 * hconsts.MillisecondsPerSecond // txs shouldn't expire gen.CustomAllocation = []*genesis.CustomAllocation{ @@ -272,7 +273,7 @@ var _ = ginkgo.BeforeSuite(func() { nil, []byte( fmt.Sprintf( - `{%s"signatureVerificationCores":%d, "rootGenerationCores":%d, "transactionExecutionCores":%d, "mempoolSize":%d, "mempoolPayerSize":%d, "verifySignatures":%t, "testMode":true}`, + `{%s"signatureVerificationCores":%d, "rootGenerationCores":%d, "transactionExecutionCores":%d, "mempoolSize":%d, "mempoolSponsorSize":%d, "verifySignatures":%t, "testMode":true}`, tracePrefix, numWorkers/3, numWorkers/3, @@ -388,8 +389,8 @@ var _ = ginkgo.Describe("load tests vm", func() { for i := 0; i < accts; i++ { tpriv, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) - trsender := tpriv.PublicKey() - tsender := utils.Address(trsender) + trsender := auth.NewED25519Address(tpriv.PublicKey()) + tsender := codec.MustAddressBech32(consts.HRP, trsender) senders[i] = &account{tpriv, auth.NewED25519Factory(tpriv), trsender, tsender} } }) @@ -538,7 +539,7 @@ var _ = ginkgo.Describe("load tests vm", func() { func issueSimpleTx( i *instance, - to ed25519.PublicKey, + to codec.Address, amount uint64, factory chain.AuthFactory, ) (ids.ID, error) { diff --git a/examples/morpheusvm/utils/utils.go b/examples/morpheusvm/utils/utils.go deleted file mode 100644 index 5257835624..0000000000 --- a/examples/morpheusvm/utils/utils.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package utils - -import ( - "github.com/ava-labs/hypersdk/crypto/ed25519" - - "github.com/ava-labs/hypersdk/examples/morpheusvm/consts" -) - -func Address(pk ed25519.PublicKey) string { - return ed25519.Address(consts.HRP, pk) -} - -func ParseAddress(s string) (ed25519.PublicKey, error) { - return ed25519.ParseAddress(consts.HRP, s) -} diff --git a/examples/tokenvm/:q b/examples/tokenvm/:q new file mode 100644 index 0000000000..01cf9e474d --- /dev/null +++ b/examples/tokenvm/:q @@ -0,0 +1,17 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package rpc + +import ( + "context" + + "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/hypersdk/codec" +) + +type Manager interface { + GetFaucetAddress(context.Context) (codec.Address, error) + GetChallenge(context.Context) ([]byte, uint16, error) + SolveChallenge(context.Context, codec.Address, []byte, []byte) (ids.ID, uint64, error) +} diff --git a/examples/tokenvm/actions/burn_asset.go b/examples/tokenvm/actions/burn_asset.go index 6cda8bc22e..9a9b64fce7 100644 --- a/examples/tokenvm/actions/burn_asset.go +++ b/examples/tokenvm/actions/burn_asset.go @@ -13,7 +13,6 @@ import ( "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/auth" "github.com/ava-labs/hypersdk/examples/tokenvm/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -33,11 +32,10 @@ func (*BurnAsset) GetTypeID() uint8 { return burnAssetID } -func (b *BurnAsset) StateKeys(rauth chain.Auth, _ ids.ID) []string { - actor := auth.GetActor(rauth) +func (b *BurnAsset) StateKeys(auth chain.Auth, _ ids.ID) []string { return []string{ string(storage.AssetKey(b.Asset)), - string(storage.BalanceKey(actor, b.Asset)), + string(storage.BalanceKey(auth.Actor(), b.Asset)), } } @@ -54,15 +52,14 @@ func (b *BurnAsset) Execute( _ chain.Rules, mu state.Mutable, _ int64, - rauth chain.Auth, + auth chain.Auth, _ ids.ID, _ bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) if b.Value == 0 { return false, BurnComputeUnits, OutputValueZero, nil, nil } - if err := storage.SubBalance(ctx, mu, actor, b.Asset, b.Value); err != nil { + if err := storage.SubBalance(ctx, mu, auth.Actor(), b.Asset, b.Value); err != nil { return false, BurnComputeUnits, utils.ErrBytes(err), nil, nil } exists, symbol, decimals, metadata, supply, owner, warp, err := storage.GetAsset(ctx, mu, b.Asset) diff --git a/examples/tokenvm/actions/close_order.go b/examples/tokenvm/actions/close_order.go index 50ba920582..1ec79b3bb7 100644 --- a/examples/tokenvm/actions/close_order.go +++ b/examples/tokenvm/actions/close_order.go @@ -11,7 +11,6 @@ import ( "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/auth" "github.com/ava-labs/hypersdk/examples/tokenvm/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -32,11 +31,10 @@ func (*CloseOrder) GetTypeID() uint8 { return closeOrderID } -func (c *CloseOrder) StateKeys(rauth chain.Auth, _ ids.ID) []string { - actor := auth.GetActor(rauth) +func (c *CloseOrder) StateKeys(auth chain.Auth, _ ids.ID) []string { return []string{ string(storage.OrderKey(c.Order)), - string(storage.BalanceKey(actor, c.Out)), + string(storage.BalanceKey(auth.Actor(), c.Out)), } } @@ -53,11 +51,10 @@ func (c *CloseOrder) Execute( _ chain.Rules, mu state.Mutable, _ int64, - rauth chain.Auth, + auth chain.Auth, _ ids.ID, _ bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) exists, _, _, out, _, remaining, owner, err := storage.GetOrder(ctx, mu, c.Order) if err != nil { return false, CloseOrderComputeUnits, utils.ErrBytes(err), nil, nil @@ -65,7 +62,7 @@ func (c *CloseOrder) Execute( if !exists { return false, CloseOrderComputeUnits, OutputOrderMissing, nil, nil } - if owner != actor { + if owner != auth.Actor() { return false, CloseOrderComputeUnits, OutputUnauthorized, nil, nil } if out != c.Out { @@ -74,7 +71,7 @@ func (c *CloseOrder) Execute( if err := storage.DeleteOrder(ctx, mu, c.Order); err != nil { return false, CloseOrderComputeUnits, utils.ErrBytes(err), nil, nil } - if err := storage.AddBalance(ctx, mu, actor, c.Out, remaining, true); err != nil { + if err := storage.AddBalance(ctx, mu, auth.Actor(), c.Out, remaining, true); err != nil { return false, CloseOrderComputeUnits, utils.ErrBytes(err), nil, nil } return true, CloseOrderComputeUnits, nil, nil, nil diff --git a/examples/tokenvm/actions/create_asset.go b/examples/tokenvm/actions/create_asset.go index de6a294351..ffdcc0dfc2 100644 --- a/examples/tokenvm/actions/create_asset.go +++ b/examples/tokenvm/actions/create_asset.go @@ -11,7 +11,6 @@ import ( "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/auth" "github.com/ava-labs/hypersdk/examples/tokenvm/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -48,11 +47,10 @@ func (c *CreateAsset) Execute( _ chain.Rules, mu state.Mutable, _ int64, - rauth chain.Auth, + auth chain.Auth, txID ids.ID, _ bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) if len(c.Symbol) == 0 { return false, CreateAssetComputeUnits, OutputSymbolEmpty, nil, nil } @@ -70,7 +68,7 @@ func (c *CreateAsset) Execute( } // 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 { + if err := storage.SetAsset(ctx, mu, txID, c.Symbol, c.Decimals, c.Metadata, 0, auth.Actor(), false); err != nil { return false, CreateAssetComputeUnits, utils.ErrBytes(err), nil, nil } return true, CreateAssetComputeUnits, nil, nil, nil diff --git a/examples/tokenvm/actions/create_order.go b/examples/tokenvm/actions/create_order.go index 8fb00bc989..877f98c6be 100644 --- a/examples/tokenvm/actions/create_order.go +++ b/examples/tokenvm/actions/create_order.go @@ -12,7 +12,6 @@ import ( "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/auth" "github.com/ava-labs/hypersdk/examples/tokenvm/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -52,10 +51,9 @@ func (*CreateOrder) GetTypeID() uint8 { return createOrderID } -func (c *CreateOrder) StateKeys(rauth chain.Auth, txID ids.ID) []string { - actor := auth.GetActor(rauth) +func (c *CreateOrder) StateKeys(auth chain.Auth, txID ids.ID) []string { return []string{ - string(storage.BalanceKey(actor, c.Out)), + string(storage.BalanceKey(auth.Actor(), c.Out)), string(storage.OrderKey(txID)), } } @@ -73,11 +71,10 @@ func (c *CreateOrder) Execute( _ chain.Rules, mu state.Mutable, _ int64, - rauth chain.Auth, + auth chain.Auth, txID ids.ID, _ bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) if c.In == c.Out { return false, CreateOrderComputeUnits, OutputSameInOut, nil, nil } @@ -93,10 +90,10 @@ func (c *CreateOrder) Execute( if c.Supply%c.OutTick != 0 { return false, CreateOrderComputeUnits, OutputSupplyMisaligned, nil, nil } - if err := storage.SubBalance(ctx, mu, actor, c.Out, c.Supply); err != nil { + if err := storage.SubBalance(ctx, mu, auth.Actor(), c.Out, c.Supply); err != nil { return false, CreateOrderComputeUnits, utils.ErrBytes(err), nil, nil } - if err := storage.SetOrder(ctx, mu, txID, c.In, c.InTick, c.Out, c.OutTick, c.Supply, actor); err != nil { + if err := storage.SetOrder(ctx, mu, txID, c.In, c.InTick, c.Out, c.OutTick, c.Supply, auth.Actor()); err != nil { return false, CreateOrderComputeUnits, utils.ErrBytes(err), nil, nil } return true, CreateOrderComputeUnits, nil, nil, nil diff --git a/examples/tokenvm/actions/export_asset.go b/examples/tokenvm/actions/export_asset.go index d047be6ab0..37d366a572 100644 --- a/examples/tokenvm/actions/export_asset.go +++ b/examples/tokenvm/actions/export_asset.go @@ -13,8 +13,6 @@ import ( "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "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/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -23,34 +21,33 @@ import ( var _ chain.Action = (*ExportAsset)(nil) type ExportAsset struct { - To ed25519.PublicKey `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"` + 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(rauth chain.Auth, _ ids.ID) []string { - actor := auth.GetActor(rauth) +func (e *ExportAsset) StateKeys(auth chain.Auth, _ ids.ID) []string { if e.Return { return []string{ string(storage.AssetKey(e.Asset)), - string(storage.BalanceKey(actor, e.Asset)), + string(storage.BalanceKey(auth.Actor(), e.Asset)), } } return []string{ string(storage.AssetKey(e.Asset)), string(storage.LoanKey(e.Asset, e.Destination)), - string(storage.BalanceKey(actor, e.Asset)), + string(storage.BalanceKey(auth.Actor(), e.Asset)), } } @@ -68,7 +65,7 @@ func (*ExportAsset) OutputsWarpMessage() bool { func (e *ExportAsset) executeReturn( ctx context.Context, mu state.Mutable, - actor ed25519.PublicKey, + 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) @@ -97,7 +94,7 @@ func (e *ExportAsset) executeReturn( return false, ExportAssetComputeUnits, utils.ErrBytes(err), nil, nil } if newSupply > 0 { - if err := storage.SetAsset(ctx, mu, e.Asset, symbol, decimals, metadata, newSupply, ed25519.EmptyPublicKey, true); err != nil { + 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 { @@ -146,7 +143,7 @@ func (e *ExportAsset) executeReturn( func (e *ExportAsset) executeLoan( ctx context.Context, mu state.Mutable, - actor ed25519.PublicKey, + actor codec.Address, txID ids.ID, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { exists, symbol, decimals, _, _, _, isWarp, err := storage.GetAsset(ctx, mu, e.Asset) @@ -205,11 +202,10 @@ func (e *ExportAsset) Execute( _ chain.Rules, mu state.Mutable, _ int64, - rauth chain.Auth, + auth chain.Auth, txID ids.ID, _ bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) if e.Value == 0 { return false, ExportAssetComputeUnits, OutputValueZero, nil, nil } @@ -220,9 +216,9 @@ func (e *ExportAsset) Execute( } // TODO: check if destination is ourselves if e.Return { - return e.executeReturn(ctx, mu, actor, txID) + return e.executeReturn(ctx, mu, auth.Actor(), txID) } - return e.executeLoan(ctx, mu, actor, txID) + return e.executeLoan(ctx, mu, auth.Actor(), txID) } func (*ExportAsset) MaxComputeUnits(chain.Rules) uint64 { @@ -230,7 +226,7 @@ func (*ExportAsset) MaxComputeUnits(chain.Rules) uint64 { } func (*ExportAsset) Size() int { - return ed25519.PublicKeyLen + consts.IDLen + + return codec.AddressLen + consts.IDLen + consts.Uint64Len + consts.BoolLen + consts.Uint64Len + /* op bits */ consts.Uint64Len + consts.Uint64Len + consts.IDLen + consts.Uint64Len + @@ -238,7 +234,7 @@ func (*ExportAsset) Size() int { } func (e *ExportAsset) Marshal(p *codec.Packer) { - p.PackPublicKey(e.To) + p.PackAddress(e.To) p.PackID(e.Asset) p.PackUint64(e.Value) p.PackBool(e.Return) @@ -254,8 +250,8 @@ func (e *ExportAsset) Marshal(p *codec.Packer) { func UnmarshalExportAsset(p *codec.Packer, _ *warp.Message) (chain.Action, error) { var export ExportAsset - p.UnpackPublicKey(false, &export.To) // can transfer to blackhole - p.UnpackID(false, &export.Asset) // may export native + p.UnpackAddress(&export.To) + p.UnpackID(false, &export.Asset) // may export native export.Value = p.UnpackUint64(true) export.Return = p.UnpackBool() op := p.NewOptionalReader() diff --git a/examples/tokenvm/actions/fill_order.go b/examples/tokenvm/actions/fill_order.go index 98cbca41c5..d7cb99dc49 100644 --- a/examples/tokenvm/actions/fill_order.go +++ b/examples/tokenvm/actions/fill_order.go @@ -13,8 +13,6 @@ import ( "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "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/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -28,7 +26,7 @@ type FillOrder struct { // [Owner] is the owner of the order and the recipient of the trade // proceeds. - Owner ed25519.PublicKey `json:"owner"` + Owner codec.Address `json:"owner"` // [In] is the asset that will be sent to the owner from the fill. We need to provide this to // populate [StateKeys]. @@ -46,13 +44,12 @@ func (*FillOrder) GetTypeID() uint8 { return fillOrderID } -func (f *FillOrder) StateKeys(rauth chain.Auth, _ ids.ID) []string { - actor := auth.GetActor(rauth) +func (f *FillOrder) StateKeys(auth chain.Auth, _ ids.ID) []string { return []string{ string(storage.OrderKey(f.Order)), string(storage.BalanceKey(f.Owner, f.In)), - string(storage.BalanceKey(actor, f.In)), - string(storage.BalanceKey(actor, f.Out)), + string(storage.BalanceKey(auth.Actor(), f.In)), + string(storage.BalanceKey(auth.Actor(), f.Out)), } } @@ -69,11 +66,10 @@ func (f *FillOrder) Execute( _ chain.Rules, mu state.Mutable, _ int64, - rauth chain.Auth, + auth chain.Auth, _ ids.ID, _ bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) 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 @@ -133,13 +129,13 @@ func (f *FillOrder) Execute( // Don't allow free trades (can happen due to refund rounding) return false, NoFillOrderComputeUnits, OutputInsufficientInput, nil, nil } - if err := storage.SubBalance(ctx, mu, actor, f.In, inputAmount); err != nil { + if err := storage.SubBalance(ctx, mu, auth.Actor(), f.In, inputAmount); err != nil { return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil } if err := storage.AddBalance(ctx, mu, f.Owner, f.In, inputAmount, true); err != nil { return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil } - if err := storage.AddBalance(ctx, mu, actor, f.Out, outputAmount, true); err != nil { + if err := storage.AddBalance(ctx, mu, auth.Actor(), f.Out, outputAmount, true); err != nil { return false, NoFillOrderComputeUnits, utils.ErrBytes(err), nil, nil } if shouldDelete { @@ -164,12 +160,12 @@ func (*FillOrder) MaxComputeUnits(chain.Rules) uint64 { } func (*FillOrder) Size() int { - return consts.IDLen*3 + ed25519.PublicKeyLen + consts.Uint64Len + return consts.IDLen*3 + codec.AddressLen + consts.Uint64Len } func (f *FillOrder) Marshal(p *codec.Packer) { p.PackID(f.Order) - p.PackPublicKey(f.Owner) + p.PackAddress(f.Owner) p.PackID(f.In) p.PackID(f.Out) p.PackUint64(f.Value) @@ -178,7 +174,7 @@ func (f *FillOrder) Marshal(p *codec.Packer) { func UnmarshalFillOrder(p *codec.Packer, _ *warp.Message) (chain.Action, error) { var fill FillOrder p.UnpackID(true, &fill.Order) - p.UnpackPublicKey(true, &fill.Owner) + p.UnpackAddress(&fill.Owner) p.UnpackID(false, &fill.In) // empty ID is the native asset p.UnpackID(false, &fill.Out) // empty ID is the native asset fill.Value = p.UnpackUint64(true) diff --git a/examples/tokenvm/actions/import_asset.go b/examples/tokenvm/actions/import_asset.go index 8d7f67a082..58029d1da3 100644 --- a/examples/tokenvm/actions/import_asset.go +++ b/examples/tokenvm/actions/import_asset.go @@ -14,8 +14,6 @@ import ( "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "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/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -40,11 +38,10 @@ func (*ImportAsset) GetTypeID() uint8 { return importAssetID } -func (i *ImportAsset) StateKeys(rauth chain.Auth, _ ids.ID) []string { +func (i *ImportAsset) StateKeys(auth chain.Auth, _ ids.ID) []string { var ( keys []string assetID ids.ID - actor = auth.GetActor(rauth) ) if i.warpTransfer.Return { assetID = i.warpTransfer.Asset @@ -64,14 +61,14 @@ func (i *ImportAsset) StateKeys(rauth chain.Auth, _ ids.ID) []string { // If the [warpTransfer] specified a reward, we add the state key to make // sure it is paid. if i.warpTransfer.Reward > 0 { - keys = append(keys, string(storage.BalanceKey(actor, assetID))) + keys = append(keys, string(storage.BalanceKey(auth.Actor(), assetID))) } // If the [warpTransfer] requests a swap, we add the state keys to transfer // the required balances. if i.Fill && i.warpTransfer.SwapIn > 0 { - keys = append(keys, string(storage.BalanceKey(actor, i.warpTransfer.AssetOut))) - keys = append(keys, string(storage.BalanceKey(actor, assetID))) + keys = append(keys, string(storage.BalanceKey(auth.Actor(), i.warpTransfer.AssetOut))) + keys = append(keys, string(storage.BalanceKey(auth.Actor(), assetID))) keys = append(keys, string(storage.BalanceKey(i.warpTransfer.To, i.warpTransfer.AssetOut))) } return keys @@ -105,7 +102,7 @@ func (*ImportAsset) OutputsWarpMessage() bool { func (i *ImportAsset) executeMint( ctx context.Context, mu state.Mutable, - actor ed25519.PublicKey, + actor codec.Address, ) []byte { asset := ImportedAssetID(i.warpTransfer.Asset, i.warpMessage.SourceChainID) exists, symbol, decimals, metadata, supply, _, warp, err := storage.GetAsset(ctx, mu, asset) @@ -129,7 +126,7 @@ func (i *ImportAsset) executeMint( if err != nil { return utils.ErrBytes(err) } - if err := storage.SetAsset(ctx, mu, asset, symbol, decimals, metadata, newSupply, ed25519.EmptyPublicKey, true); err != nil { + 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 { @@ -146,7 +143,7 @@ func (i *ImportAsset) executeMint( func (i *ImportAsset) executeReturn( ctx context.Context, mu state.Mutable, - actor ed25519.PublicKey, + actor codec.Address, ) []byte { exists, symbol, decimals, _, _, _, warp, err := storage.GetAsset(ctx, mu, i.warpTransfer.Asset) if err != nil { @@ -200,11 +197,10 @@ func (i *ImportAsset) Execute( r chain.Rules, mu state.Mutable, t int64, - rauth chain.Auth, + auth chain.Auth, _ ids.ID, warpVerified bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) if !warpVerified { return false, ImportAssetComputeUnits, OutputWarpVerificationFailed, nil, nil } @@ -216,9 +212,9 @@ func (i *ImportAsset) Execute( } var output []byte if i.warpTransfer.Return { - output = i.executeReturn(ctx, mu, actor) + output = i.executeReturn(ctx, mu, auth.Actor()) } else { - output = i.executeMint(ctx, mu, actor) + output = i.executeMint(ctx, mu, auth.Actor()) } if len(output) > 0 { return false, ImportAssetComputeUnits, output, nil, nil @@ -243,10 +239,10 @@ func (i *ImportAsset) Execute( 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 { + if err := storage.AddBalance(ctx, mu, auth.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 { + if err := storage.SubBalance(ctx, mu, auth.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 { diff --git a/examples/tokenvm/actions/mint_asset.go b/examples/tokenvm/actions/mint_asset.go index 6bd62391a1..0b26d51799 100644 --- a/examples/tokenvm/actions/mint_asset.go +++ b/examples/tokenvm/actions/mint_asset.go @@ -13,8 +13,6 @@ import ( "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "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/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -24,7 +22,7 @@ var _ chain.Action = (*MintAsset)(nil) type MintAsset struct { // To is the recipient of the [Value]. - To ed25519.PublicKey `json:"to"` + To codec.Address `json:"to"` // Asset is the [TxID] that created the asset. Asset ids.ID `json:"asset"` @@ -57,11 +55,10 @@ func (m *MintAsset) Execute( _ chain.Rules, mu state.Mutable, _ int64, - rauth chain.Auth, + auth chain.Auth, _ ids.ID, _ bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) if m.Asset == ids.Empty { return false, MintAssetComputeUnits, OutputAssetIsNative, nil, nil } @@ -78,14 +75,14 @@ func (m *MintAsset) Execute( if isWarp { return false, MintAssetComputeUnits, OutputWarpAsset, nil, nil } - if owner != actor { + if owner != auth.Actor() { return false, MintAssetComputeUnits, OutputWrongOwner, nil, nil } newSupply, err := smath.Add64(supply, m.Value) if err != nil { return false, MintAssetComputeUnits, utils.ErrBytes(err), nil, nil } - if err := storage.SetAsset(ctx, mu, m.Asset, symbol, decimals, metadata, newSupply, actor, isWarp); err != nil { + if err := storage.SetAsset(ctx, mu, m.Asset, symbol, decimals, metadata, newSupply, auth.Actor(), isWarp); err != nil { return false, MintAssetComputeUnits, utils.ErrBytes(err), nil, nil } if err := storage.AddBalance(ctx, mu, m.To, m.Asset, m.Value, true); err != nil { @@ -99,19 +96,19 @@ func (*MintAsset) MaxComputeUnits(chain.Rules) uint64 { } func (*MintAsset) Size() int { - return ed25519.PublicKeyLen + consts.IDLen + consts.Uint64Len + return codec.AddressLen + consts.IDLen + consts.Uint64Len } func (m *MintAsset) Marshal(p *codec.Packer) { - p.PackPublicKey(m.To) + p.PackAddress(m.To) p.PackID(m.Asset) p.PackUint64(m.Value) } func UnmarshalMintAsset(p *codec.Packer, _ *warp.Message) (chain.Action, error) { var mint MintAsset - p.UnpackPublicKey(true, &mint.To) // cannot mint to blackhole - p.UnpackID(true, &mint.Asset) // empty ID is the native asset + p.UnpackAddress(&mint.To) + p.UnpackID(true, &mint.Asset) // empty ID is the native asset mint.Value = p.UnpackUint64(true) return &mint, p.Err() } diff --git a/examples/tokenvm/actions/transfer.go b/examples/tokenvm/actions/transfer.go index 28eb0733f1..428d43340e 100644 --- a/examples/tokenvm/actions/transfer.go +++ b/examples/tokenvm/actions/transfer.go @@ -11,8 +11,6 @@ import ( "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "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/storage" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/utils" @@ -22,7 +20,7 @@ var _ chain.Action = (*Transfer)(nil) type Transfer struct { // To is the recipient of the [Value]. - To ed25519.PublicKey `json:"to"` + To codec.Address `json:"to"` // Asset to transfer to [To]. Asset ids.ID `json:"asset"` @@ -38,9 +36,9 @@ func (*Transfer) GetTypeID() uint8 { return transferID } -func (t *Transfer) StateKeys(rauth chain.Auth, _ ids.ID) []string { +func (t *Transfer) StateKeys(auth chain.Auth, _ ids.ID) []string { return []string{ - string(storage.BalanceKey(auth.GetActor(rauth), t.Asset)), + string(storage.BalanceKey(auth.Actor(), t.Asset)), string(storage.BalanceKey(t.To, t.Asset)), } } @@ -58,18 +56,17 @@ func (t *Transfer) Execute( _ chain.Rules, mu state.Mutable, _ int64, - rauth chain.Auth, + auth chain.Auth, _ ids.ID, _ bool, ) (bool, uint64, []byte, *warp.UnsignedMessage, error) { - actor := auth.GetActor(rauth) if t.Value == 0 { return false, TransferComputeUnits, OutputValueZero, nil, nil } if len(t.Memo) > MaxMemoSize { return false, CreateAssetComputeUnits, OutputMemoTooLarge, nil, nil } - if err := storage.SubBalance(ctx, mu, actor, t.Asset, t.Value); err != nil { + if err := storage.SubBalance(ctx, mu, auth.Actor(), t.Asset, t.Value); err != nil { return false, TransferComputeUnits, utils.ErrBytes(err), nil, nil } // TODO: allow sender to configure whether they will pay to create @@ -84,11 +81,11 @@ func (*Transfer) MaxComputeUnits(chain.Rules) uint64 { } func (t *Transfer) Size() int { - return ed25519.PublicKeyLen + consts.IDLen + consts.Uint64Len + codec.BytesLen(t.Memo) + return codec.AddressLen + consts.IDLen + consts.Uint64Len + codec.BytesLen(t.Memo) } func (t *Transfer) Marshal(p *codec.Packer) { - p.PackPublicKey(t.To) + p.PackAddress(t.To) p.PackID(t.Asset) p.PackUint64(t.Value) p.PackBytes(t.Memo) @@ -96,8 +93,8 @@ func (t *Transfer) Marshal(p *codec.Packer) { func UnmarshalTransfer(p *codec.Packer, _ *warp.Message) (chain.Action, error) { var transfer Transfer - p.UnpackPublicKey(false, &transfer.To) // can transfer to blackhole - p.UnpackID(false, &transfer.Asset) // empty ID is the native asset + p.UnpackAddress(&transfer.To) + p.UnpackID(false, &transfer.Asset) // empty ID is the native asset transfer.Value = p.UnpackUint64(true) p.UnpackBytes(MaxMemoSize, false, &transfer.Memo) return &transfer, p.Err() diff --git a/examples/tokenvm/actions/warp_transfer.go b/examples/tokenvm/actions/warp_transfer.go index bc8f955e97..03c448df21 100644 --- a/examples/tokenvm/actions/warp_transfer.go +++ b/examples/tokenvm/actions/warp_transfer.go @@ -8,16 +8,15 @@ import ( "github.com/ava-labs/hypersdk/chain" "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/utils" ) type WarpTransfer struct { - To ed25519.PublicKey `json:"to"` - Symbol []byte `json:"symbol"` - Decimals uint8 `json:"decimals"` - Asset ids.ID `json:"asset"` - Value uint64 `json:"value"` + 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. @@ -47,7 +46,7 @@ type WarpTransfer struct { } func (w *WarpTransfer) size() int { - return ed25519.PublicKeyLen + codec.BytesLen(w.Symbol) + consts.Uint8Len + consts.IDLen + + 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 + @@ -56,7 +55,7 @@ func (w *WarpTransfer) size() int { func (w *WarpTransfer) Marshal() ([]byte, error) { p := codec.NewWriter(w.size(), w.size()) - p.PackPublicKey(w.To) + p.PackAddress(w.To) p.PackBytes(w.Symbol) p.PackByte(w.Decimals) p.PackID(w.Asset) @@ -86,7 +85,7 @@ func ImportedAssetMetadata(assetID ids.ID, sourceChainID ids.ID) []byte { } func UnmarshalWarpTransfer(b []byte) (*WarpTransfer, error) { - maxWarpTransferSize := ed25519.PublicKeyLen + codec.BytesLenSize(MaxSymbolSize) + consts.Uint8Len + consts.IDLen + + 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 + @@ -94,7 +93,7 @@ func UnmarshalWarpTransfer(b []byte) (*WarpTransfer, error) { var transfer WarpTransfer p := codec.NewReader(b, maxWarpTransferSize) - p.UnpackPublicKey(false, &transfer.To) + p.UnpackAddress(&transfer.To) p.UnpackBytes(MaxSymbolSize, true, &transfer.Symbol) transfer.Decimals = p.UnpackByte() p.UnpackID(false, &transfer.Asset) diff --git a/examples/tokenvm/auth/ed25519.go b/examples/tokenvm/auth/ed25519.go index b2991880e0..f26fa30da5 100644 --- a/examples/tokenvm/auth/ed25519.go +++ b/examples/tokenvm/auth/ed25519.go @@ -15,6 +15,7 @@ import ( "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/examples/tokenvm/storage" "github.com/ava-labs/hypersdk/state" + "github.com/ava-labs/hypersdk/utils" ) var _ chain.Auth = (*ED25519)(nil) @@ -27,6 +28,15 @@ const ( type ED25519 struct { Signer ed25519.PublicKey `json:"signer"` Signature ed25519.Signature `json:"signature"` + + addr codec.Address +} + +func (d *ED25519) address() codec.Address { + if d.addr == codec.EmptyAddress { + d.addr = NewED25519Address(d.Signer) + } + return d.addr } func (*ED25519) GetTypeID() uint8 { @@ -44,7 +54,7 @@ func (*ED25519) ValidRange(chain.Rules) (int64, int64) { func (d *ED25519) StateKeys() []string { return []string{ // We always pay fees with the native asset (which is [ids.Empty]) - string(storage.BalanceKey(d.Signer, ids.Empty)), + string(storage.BalanceKey(d.address(), ids.Empty)), } } @@ -66,8 +76,12 @@ func (d *ED25519) Verify( return d.MaxComputeUnits(r), nil } -func (d *ED25519) Payer() []byte { - return d.Signer[:] +func (d *ED25519) Actor() codec.Address { + return d.address() +} + +func (d *ED25519) Sponsor() codec.Address { + return d.address() } func (*ED25519) Size() int { @@ -75,14 +89,16 @@ func (*ED25519) Size() int { } func (d *ED25519) Marshal(p *codec.Packer) { - p.PackPublicKey(d.Signer) - p.PackSignature(d.Signature) + p.PackFixedBytes(d.Signer[:]) + p.PackFixedBytes(d.Signature[:]) } func UnmarshalED25519(p *codec.Packer, _ *warp.Message) (chain.Auth, error) { var d ED25519 - p.UnpackPublicKey(true, &d.Signer) - p.UnpackSignature(&d.Signature) + signer := d.Signer[:] // avoid allocating additional memory + p.UnpackFixedBytes(ed25519.PublicKeyLen, &signer) + signature := d.Signature[:] // avoid allocating additional memory + p.UnpackFixedBytes(ed25519.SignatureLen, &signature) return &d, p.Err() } @@ -91,7 +107,7 @@ func (d *ED25519) CanDeduct( im state.Immutable, amount uint64, ) error { - bal, err := storage.GetBalance(ctx, im, d.Signer, ids.Empty) + bal, err := storage.GetBalance(ctx, im, d.address(), ids.Empty) if err != nil { return err } @@ -106,7 +122,7 @@ func (d *ED25519) Deduct( mu state.Mutable, amount uint64, ) error { - return storage.SubBalance(ctx, mu, d.Signer, ids.Empty, amount) + return storage.SubBalance(ctx, mu, d.address(), ids.Empty, amount) } func (d *ED25519) Refund( @@ -115,7 +131,7 @@ func (d *ED25519) Refund( amount uint64, ) error { // Don't create account if it doesn't exist (may have sent all funds). - return storage.AddBalance(ctx, mu, d.Signer, ids.Empty, amount, false) + return storage.AddBalance(ctx, mu, d.address(), ids.Empty, amount, false) } var _ chain.AuthFactory = (*ED25519Factory)(nil) @@ -130,7 +146,7 @@ type ED25519Factory struct { func (d *ED25519Factory) Sign(msg []byte, _ chain.Action) (chain.Auth, error) { sig := ed25519.Sign(msg, d.priv) - return &ED25519{d.priv.PublicKey(), sig}, nil + return &ED25519{Signer: d.priv.PublicKey(), Signature: sig}, nil } func (*ED25519Factory) MaxUnits() (uint64, uint64, []uint16) { @@ -148,8 +164,12 @@ func (*ED25519AuthEngine) GetBatchVerifier(cores int, count int) chain.AuthBatch } func (*ED25519AuthEngine) Cache(auth chain.Auth) { - pk := GetSigner(auth) - ed25519.CachePublicKey(pk) + // This should never not happen but we perform this check + // to avoid a panic. + pauth, ok := auth.(*ED25519) + if ok { + ed25519.CachePublicKey(pauth.Signer) + } } type ED25519Batch struct { @@ -187,3 +207,7 @@ func (b *ED25519Batch) Done() []func() error { } return []func() error{b.batch.VerifyAsync()} } + +func NewED25519Address(pk ed25519.PublicKey) codec.Address { + return codec.CreateAddress(ed25519ID, utils.ToID(pk[:])) +} diff --git a/examples/tokenvm/auth/helpers.go b/examples/tokenvm/auth/helpers.go deleted file mode 100644 index 340f44f367..0000000000 --- a/examples/tokenvm/auth/helpers.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package auth - -import ( - "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/crypto/ed25519" -) - -func GetActor(auth chain.Auth) ed25519.PublicKey { - switch a := auth.(type) { - case *ED25519: - return a.Signer - default: - return ed25519.EmptyPublicKey - } -} - -func GetSigner(auth chain.Auth) ed25519.PublicKey { - switch a := auth.(type) { - case *ED25519: - return a.Signer - default: - return ed25519.EmptyPublicKey - } -} diff --git a/examples/tokenvm/cmd/token-cli/cmd/action.go b/examples/tokenvm/cmd/token-cli/cmd/action.go index 8a62740940..b5e5cfa614 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/action.go +++ b/examples/tokenvm/cmd/token-cli/cmd/action.go @@ -13,11 +13,12 @@ import ( "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/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/pubsub" "github.com/ava-labs/hypersdk/rpc" hutils "github.com/ava-labs/hypersdk/utils" @@ -54,7 +55,7 @@ var fundFaucetCmd = &cobra.Command{ } // Get balance - _, decimals, balance, _, err := handler.GetAssetInfo(ctx, tcli, priv.PublicKey(), ids.Empty, true) + _, decimals, balance, _, err := handler.GetAssetInfo(ctx, tcli, priv.Address, ids.Empty, true) if balance == 0 || err != nil { return err } @@ -72,12 +73,12 @@ var fundFaucetCmd = &cobra.Command{ } // Generate transaction - pk, err := utils.ParseAddress(faucetAddress) + addr, err := codec.ParseAddressBech32(tconsts.HRP, faucetAddress) if err != nil { return err } if _, _, err = sendAndWait(ctx, nil, &actions.Transfer{ - To: pk, + To: addr, Asset: ids.Empty, Value: amount, }, cli, scli, tcli, factory, true); err != nil { @@ -102,7 +103,7 @@ var transferCmd = &cobra.Command{ if err != nil { return err } - _, decimals, balance, _, err := handler.GetAssetInfo(ctx, tcli, priv.PublicKey(), assetID, true) + _, decimals, balance, _, err := handler.GetAssetInfo(ctx, tcli, priv.Address, assetID, true) if balance == 0 || err != nil { return err } @@ -206,7 +207,7 @@ var mintAssetCmd = &cobra.Command{ hutils.Outf("{{red}}exiting...{{/}}\n") return nil } - if owner != utils.Address(priv.PublicKey()) { + 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") return nil @@ -328,7 +329,7 @@ var createOrderCmd = &cobra.Command{ if err != nil { return err } - _, decimals, balance, _, err := handler.GetAssetInfo(ctx, tcli, priv.PublicKey(), outAssetID, true) + _, decimals, balance, _, err := handler.GetAssetInfo(ctx, tcli, priv.Address, outAssetID, true) if balance == 0 || err != nil { return err } @@ -387,7 +388,7 @@ var fillOrderCmd = &cobra.Command{ if err != nil { return err } - inSymbol, inDecimals, balance, _, err := handler.GetAssetInfo(ctx, tcli, priv.PublicKey(), inAssetID, true) + inSymbol, inDecimals, balance, _, err := handler.GetAssetInfo(ctx, tcli, priv.Address, inAssetID, true) if balance == 0 || err != nil { return err } @@ -397,7 +398,7 @@ var fillOrderCmd = &cobra.Command{ if err != nil { return err } - outSymbol, outDecimals, _, _, err := handler.GetAssetInfo(ctx, tcli, priv.PublicKey(), outAssetID, false) + outSymbol, outDecimals, _, _, err := handler.GetAssetInfo(ctx, tcli, priv.Address, outAssetID, false) if err != nil { return err } @@ -475,7 +476,7 @@ var fillOrderCmd = &cobra.Command{ return err } - owner, err := utils.ParseAddress(order.Owner) + owner, err := codec.ParseAddressBech32(tconsts.HRP, order.Owner) if err != nil { return err } @@ -552,7 +553,7 @@ func performImport( hutils.ToID( msg.UnsignedMessage.Payload, ), - utils.Address(wt.To), + codec.MustAddressBech32(tconsts.HRP, wt.To), wt.Asset, wt.Symbol, outputAssetID, @@ -635,7 +636,7 @@ var exportAssetCmd = &cobra.Command{ if err != nil { return err } - _, decimals, balance, sourceChainID, err := handler.GetAssetInfo(ctx, tcli, priv.PublicKey(), assetID, true) + _, decimals, balance, sourceChainID, err := handler.GetAssetInfo(ctx, tcli, priv.Address, assetID, true) if balance == 0 || err != nil { return err } @@ -702,7 +703,7 @@ var exportAssetCmd = &cobra.Command{ return err } dcli := trpc.NewJSONRPCClient(uris[0], networkID, destination) - _, decimals, _, _, err := handler.GetAssetInfo(ctx, dcli, priv.PublicKey(), assetOut, false) + _, decimals, _, _, err := handler.GetAssetInfo(ctx, dcli, priv.Address, assetOut, false) if err != nil { return err } diff --git a/examples/tokenvm/cmd/token-cli/cmd/handler.go b/examples/tokenvm/cmd/token-cli/cmd/handler.go index 05b79b95db..c3fee8c16c 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/handler.go +++ b/examples/tokenvm/cmd/token-cli/cmd/handler.go @@ -7,13 +7,14 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" + "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" trpc "github.com/ava-labs/hypersdk/examples/tokenvm/rpc" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/pubsub" "github.com/ava-labs/hypersdk/rpc" hutils "github.com/ava-labs/hypersdk/utils" @@ -36,7 +37,7 @@ func (h *Handler) Root() *cli.Handler { func (*Handler) GetAssetInfo( ctx context.Context, cli *trpc.JSONRPCClient, - publicKey ed25519.PublicKey, + addr codec.Address, assetID ids.ID, checkBalance bool, ) ([]byte, uint8, uint64, ids.ID, error) { @@ -74,14 +75,17 @@ func (*Handler) GetAssetInfo( if !checkBalance { return symbol, decimals, 0, sourceChainID, nil } - addr := utils.Address(publicKey) - balance, err := cli.Balance(ctx, addr, assetID) + saddr, err := codec.AddressBech32(consts.HRP, addr) + if err != nil { + return nil, 0, 0, ids.Empty, err + } + balance, err := cli.Balance(ctx, saddr, assetID) if err != nil { return nil, 0, 0, ids.Empty, err } if balance == 0 { hutils.Outf("{{red}}balance:{{/}} 0 %s\n", assetID) - hutils.Outf("{{red}}please send funds to %s{{/}}\n", addr) + hutils.Outf("{{red}}please send funds to %s{{/}}\n", saddr) hutils.Outf("{{red}}exiting...{{/}}\n") } else { hutils.Outf( @@ -94,22 +98,22 @@ func (*Handler) GetAssetInfo( } func (h *Handler) DefaultActor() ( - ids.ID, ed25519.PrivateKey, *auth.ED25519Factory, + ids.ID, *cli.PrivateKey, chain.AuthFactory, *rpc.JSONRPCClient, *rpc.WebSocketClient, *trpc.JSONRPCClient, error, ) { - priv, err := h.h.GetDefaultKey(true) + addr, priv, err := h.h.GetDefaultKey(true) if err != nil { - return ids.Empty, ed25519.EmptyPrivateKey, nil, nil, nil, nil, err + return ids.Empty, nil, nil, nil, nil, nil, err } chainID, uris, err := h.h.GetDefaultChain(true) if err != nil { - return ids.Empty, ed25519.EmptyPrivateKey, nil, nil, nil, nil, err + return ids.Empty, nil, nil, nil, nil, nil, err } // For [defaultActor], we always send requests to the first returned URI. - cli := rpc.NewJSONRPCClient(uris[0]) - networkID, _, _, err := cli.Network(context.TODO()) + jcli := rpc.NewJSONRPCClient(uris[0]) + networkID, _, _, err := jcli.Network(context.TODO()) if err != nil { - return ids.Empty, ed25519.EmptyPrivateKey, nil, nil, nil, nil, err + return ids.Empty, nil, nil, nil, nil, nil, err } scli, err := rpc.NewWebSocketClient( uris[0], @@ -118,9 +122,9 @@ func (h *Handler) DefaultActor() ( pubsub.MaxReadMessageSize, ) if err != nil { - return ids.Empty, ed25519.EmptyPrivateKey, nil, nil, nil, nil, err + return ids.Empty, nil, nil, nil, nil, nil, err } - return chainID, priv, auth.NewED25519Factory(priv), cli, scli, + return chainID, &cli.PrivateKey{Address: addr, Bytes: priv}, auth.NewED25519Factory(ed25519.PrivateKey(priv)), jcli, scli, trpc.NewJSONRPCClient( uris[0], networkID, @@ -148,10 +152,10 @@ func (*Controller) Decimals() uint8 { return consts.Decimals } -func (*Controller) Address(pk ed25519.PublicKey) string { - return utils.Address(pk) +func (*Controller) Address(addr codec.Address) string { + return codec.MustAddressBech32(consts.HRP, addr) } -func (*Controller) ParseAddress(address string) (ed25519.PublicKey, error) { - return utils.ParseAddress(address) +func (*Controller) ParseAddress(address string) (codec.Address, error) { + return codec.ParseAddressBech32(consts.HRP, address) } diff --git a/examples/tokenvm/cmd/token-cli/cmd/key.go b/examples/tokenvm/cmd/token-cli/cmd/key.go index b67a59f08b..8e766e1f17 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/key.go +++ b/examples/tokenvm/cmd/token-cli/cmd/key.go @@ -8,16 +8,18 @@ import ( "time" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/hypersdk/cli" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/utils" "github.com/spf13/cobra" + "github.com/ava-labs/hypersdk/examples/tokenvm/auth" "github.com/ava-labs/hypersdk/examples/tokenvm/challenge" 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" - tutils "github.com/ava-labs/hypersdk/examples/tokenvm/utils" ) var keyCmd = &cobra.Command{ @@ -30,7 +32,25 @@ var keyCmd = &cobra.Command{ var genKeyCmd = &cobra.Command{ Use: "generate", RunE: func(*cobra.Command, []string) error { - return handler.Root().GenerateKey() + p, err := ed25519.GeneratePrivateKey() + if err != nil { + return err + } + priv := &cli.PrivateKey{ + Address: auth.NewED25519Address(p.PublicKey()), + Bytes: p[:], + } + if err := handler.h.StoreKey(priv); err != nil { + return err + } + if err := handler.h.StoreDefaultKey(priv.Address); err != nil { + return err + } + utils.Outf( + "{{green}}created address:{{/}} %s", + codec.MustAddressBech32(tconsts.HRP, priv.Address), + ) + return nil }, } @@ -43,7 +63,26 @@ var importKeyCmd = &cobra.Command{ return nil }, RunE: func(_ *cobra.Command, args []string) error { - return handler.Root().ImportKey(args[0]) + p, err := utils.LoadBytes(args[0], ed25519.PrivateKeyLen) + if err != nil { + return err + } + pk := ed25519.PrivateKey(p) + priv := &cli.PrivateKey{ + Address: auth.NewED25519Address(pk.PublicKey()), + Bytes: p, + } + if err := handler.h.StoreKey(priv); err != nil { + return err + } + if err := handler.h.StoreDefaultKey(priv.Address); err != nil { + return err + } + utils.Outf( + "{{green}}imported address:{{/}} %s", + codec.MustAddressBech32(tconsts.HRP, priv.Address), + ) + return nil }, } @@ -71,10 +110,10 @@ var setKeyCmd = &cobra.Command{ }, } -func lookupKeyBalance(pk ed25519.PublicKey, uri string, networkID uint32, chainID ids.ID, assetID ids.ID) error { +func lookupKeyBalance(addr codec.Address, uri string, networkID uint32, chainID ids.ID, assetID ids.ID) error { _, _, _, _, err := handler.GetAssetInfo( context.TODO(), trpc.NewJSONRPCClient(uri, networkID, chainID), - pk, assetID, true) + addr, assetID, true) return err } @@ -116,7 +155,7 @@ var faucetKeyCmd = &cobra.Command{ start := time.Now() solution, attempts := challenge.Search(salt, difficulty, numCores) utils.Outf("{{cyan}}found solution (attempts=%d, t=%s):{{/}} %x\n", attempts, time.Since(start), solution) - txID, amount, err := fcli.SolveChallenge(ctx, tutils.Address(priv.PublicKey()), salt, solution) + txID, amount, err := fcli.SolveChallenge(ctx, codec.MustAddressBech32(tconsts.HRP, priv.Address), salt, solution) if err != nil { return err } diff --git a/examples/tokenvm/cmd/token-cli/cmd/resolutions.go b/examples/tokenvm/cmd/token-cli/cmd/resolutions.go index 1e72dae87f..badd9f7b1a 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/resolutions.go +++ b/examples/tokenvm/cmd/token-cli/cmd/resolutions.go @@ -12,11 +12,10 @@ import ( "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" "github.com/ava-labs/hypersdk/examples/tokenvm/actions" - "github.com/ava-labs/hypersdk/examples/tokenvm/auth" tconsts "github.com/ava-labs/hypersdk/examples/tokenvm/consts" trpc "github.com/ava-labs/hypersdk/examples/tokenvm/rpc" - tutils "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/rpc" "github.com/ava-labs/hypersdk/utils" ) @@ -47,11 +46,11 @@ func sendAndWait( if err != nil { return false, ids.Empty, err } - if txID != tx.ID() { - continue + if txID == tx.ID() { + res = result + break } - res = result - break + utils.Outf("{{yellow}}skipping unexpected transaction:{{/}} %s\n", tx.ID()) } if printStatus { handler.Root().PrintStatus(tx.ID(), res.Success) @@ -61,7 +60,7 @@ func sendAndWait( func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result) { summaryStr := string(result.Output) - actor := auth.GetActor(tx.Auth) + actor := tx.Auth.Actor() status := "⚠️" if result.Success { status = "✅" @@ -75,7 +74,7 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result return } amountStr := utils.FormatBalance(action.Value, decimals) - summaryStr = fmt.Sprintf("%s %s -> %s", amountStr, symbol, tutils.Address(action.To)) + summaryStr = fmt.Sprintf("%s %s -> %s", amountStr, symbol, codec.MustAddressBech32(tconsts.HRP, action.To)) case *actions.BurnAsset: summaryStr = fmt.Sprintf("%d %s -> 🔥", action.Value, action.Asset) @@ -86,7 +85,7 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result return } amountStr := utils.FormatBalance(action.Value, decimals) - summaryStr = fmt.Sprintf("%s %s -> %s", amountStr, symbol, tutils.Address(action.To)) + summaryStr = fmt.Sprintf("%s %s -> %s", amountStr, symbol, codec.MustAddressBech32(tconsts.HRP, action.To)) if len(action.Memo) > 0 { summaryStr += fmt.Sprintf(" (memo: %s)", action.Memo) } @@ -134,9 +133,9 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result 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, tutils.Address(wt.To), 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, tutils.Address(wt.To), wt.Return) + 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)) @@ -155,10 +154,10 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result 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, tutils.Address(action.To), action.Return) + 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, tutils.Address(action.To), action.Return) + 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)) @@ -177,7 +176,7 @@ func handleTx(c *trpc.JSONRPCClient, tx *chain.Transaction, result *chain.Result "%s {{yellow}}%s{{/}} {{yellow}}actor:{{/}} %s {{yellow}}summary (%s):{{/}} [%s] {{yellow}}fee (max %.2f%%):{{/}} %s %s {{yellow}}consumed:{{/}} [%s]\n", status, tx.ID(), - tutils.Address(actor), + codec.MustAddressBech32(tconsts.HRP, actor), reflect.TypeOf(tx.Action), summaryStr, float64(result.Fee)/float64(tx.Base.MaxFee)*100, diff --git a/examples/tokenvm/cmd/token-cli/cmd/spam.go b/examples/tokenvm/cmd/token-cli/cmd/spam.go index fc03432fc2..3360ecd99a 100644 --- a/examples/tokenvm/cmd/token-cli/cmd/spam.go +++ b/examples/tokenvm/cmd/token-cli/cmd/spam.go @@ -8,6 +8,8 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/hypersdk/chain" + "github.com/ava-labs/hypersdk/cli" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/examples/tokenvm/actions" "github.com/ava-labs/hypersdk/examples/tokenvm/auth" @@ -37,18 +39,29 @@ var runSpamCmd = &cobra.Command{ maxFeeParsed = &v } return handler.Root().Spam(maxTxBacklog, maxFeeParsed, randomRecipient, - func(uri string, networkID uint32, chainID ids.ID) { + func(uri string, networkID uint32, chainID ids.ID) error { // createClient tclient = trpc.NewJSONRPCClient(uri, networkID, chainID) sc, err := rpc.NewWebSocketClient(uri, rpc.DefaultHandshakeTimeout, pubsub.MaxPendingMessages, pubsub.MaxReadMessageSize) if err != nil { - panic(err) + return err } sclient = sc + return nil + }, + func(priv *cli.PrivateKey) (chain.AuthFactory, error) { // getFactory + return auth.NewED25519Factory(ed25519.PrivateKey(priv.Bytes)), nil }, - func(pk ed25519.PrivateKey) chain.AuthFactory { - return auth.NewED25519Factory(pk) + func() (*cli.PrivateKey, error) { // createAccount + p, err := ed25519.GeneratePrivateKey() + if err != nil { + return nil, err + } + return &cli.PrivateKey{ + Address: auth.NewED25519Address(p.PublicKey()), + Bytes: p[:], + }, nil }, - func(choice int, address string) (uint64, error) { + func(choice int, address string) (uint64, error) { // lookupBalance balance, err := tclient.Balance(context.TODO(), address, ids.Empty) if err != nil { return 0, err @@ -62,22 +75,22 @@ var runSpamCmd = &cobra.Command{ ) return balance, err }, - func(ctx context.Context, chainID ids.ID) (chain.Parser, error) { + func(ctx context.Context, chainID ids.ID) (chain.Parser, error) { // getParser return tclient.Parser(ctx) }, - func(pk ed25519.PublicKey, amount uint64) chain.Action { + func(addr codec.Address, amount uint64) chain.Action { // getTransfer return &actions.Transfer{ - To: pk, + To: addr, Asset: ids.Empty, Value: amount, } }, - func(cli *rpc.JSONRPCClient, pk ed25519.PrivateKey) func(context.Context, uint64) error { + 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{ - To: pk.PublicKey(), + To: priv.Address, Value: count, // prevent duplicate txs - }, cli, sclient, tclient, auth.NewED25519Factory(pk), false) + }, cli, sclient, tclient, auth.NewED25519Factory(ed25519.PrivateKey(priv.Bytes)), false) return err } }, diff --git a/examples/tokenvm/cmd/token-faucet/config/config.go b/examples/tokenvm/cmd/token-faucet/config/config.go index 62e82bb3c4..4f21ef9ed3 100644 --- a/examples/tokenvm/cmd/token-faucet/config/config.go +++ b/examples/tokenvm/cmd/token-faucet/config/config.go @@ -3,7 +3,12 @@ package config -import "github.com/ava-labs/hypersdk/crypto/ed25519" +import ( + "github.com/ava-labs/hypersdk/codec" + "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/examples/tokenvm/auth" + "github.com/ava-labs/hypersdk/examples/tokenvm/consts" +) type Config struct { HTTPHost string `json:"host"` @@ -21,3 +26,11 @@ type Config struct { func (c *Config) PrivateKey() ed25519.PrivateKey { return ed25519.PrivateKey(c.PrivateKeyBytes) } + +func (c *Config) Address() codec.Address { + return auth.NewED25519Address(c.PrivateKey().PublicKey()) +} + +func (c *Config) AddressBech32() string { + return codec.MustAddressBech32(consts.HRP, c.Address()) +} diff --git a/examples/tokenvm/cmd/token-faucet/main.go b/examples/tokenvm/cmd/token-faucet/main.go index 8043511df5..e337490327 100644 --- a/examples/tokenvm/cmd/token-faucet/main.go +++ b/examples/tokenvm/cmd/token-faucet/main.go @@ -20,7 +20,6 @@ import ( "github.com/ava-labs/hypersdk/examples/tokenvm/cmd/token-faucet/config" "github.com/ava-labs/hypersdk/examples/tokenvm/cmd/token-faucet/manager" frpc "github.com/ava-labs/hypersdk/examples/tokenvm/cmd/token-faucet/rpc" - tutils "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/server" "github.com/ava-labs/hypersdk/utils" "go.uber.org/zap" @@ -86,9 +85,9 @@ func main() { if err := os.WriteFile(configPath, b, fi.Mode().Perm()); err != nil { fatal(log, "cannot write new config", zap.Error(err)) } - log.Info("created new faucet address", zap.String("address", tutils.Address(priv.PublicKey()))) + log.Info("created new faucet address", zap.String("address", c.AddressBech32())) } else { - log.Info("loaded faucet address", zap.String("address", tutils.Address(c.PrivateKey().PublicKey()))) + log.Info("loaded faucet address", zap.String("address", c.AddressBech32())) } // Create server diff --git a/examples/tokenvm/cmd/token-faucet/manager/manager.go b/examples/tokenvm/cmd/token-faucet/manager/manager.go index ff27b1659d..b445faad52 100644 --- a/examples/tokenvm/cmd/token-faucet/manager/manager.go +++ b/examples/tokenvm/cmd/token-faucet/manager/manager.go @@ -14,14 +14,13 @@ import ( "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/set" "github.com/ava-labs/avalanchego/utils/timer" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/examples/tokenvm/actions" "github.com/ava-labs/hypersdk/examples/tokenvm/auth" "github.com/ava-labs/hypersdk/examples/tokenvm/challenge" "github.com/ava-labs/hypersdk/examples/tokenvm/cmd/token-faucet/config" "github.com/ava-labs/hypersdk/examples/tokenvm/consts" trpc "github.com/ava-labs/hypersdk/examples/tokenvm/rpc" - tutils "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/rpc" "github.com/ava-labs/hypersdk/utils" "go.uber.org/zap" @@ -60,13 +59,12 @@ func New(logger logging.Logger, config *config.Config) (*Manager, error) { if err != nil { return nil, err } - addr := tutils.Address(m.config.PrivateKey().PublicKey()) - bal, err := tcli.Balance(ctx, addr, ids.Empty) + bal, err := tcli.Balance(ctx, m.config.AddressBech32(), ids.Empty) if err != nil { return nil, err } m.log.Info("faucet initialized", - zap.String("address", addr), + zap.String("address", m.config.AddressBech32()), zap.Uint16("difficulty", m.difficulty), zap.String("balance", utils.FormatBalance(bal, consts.Decimals)), ) @@ -108,8 +106,8 @@ func (m *Manager) updateDifficulty() { m.t.SetTimeoutIn(time.Duration(m.config.TargetDurationPerSalt) * time.Second) } -func (m *Manager) GetFaucetAddress(_ context.Context) (ed25519.PublicKey, error) { - return m.config.PrivateKey().PublicKey(), nil +func (m *Manager) GetFaucetAddress(_ context.Context) (codec.Address, error) { + return m.config.Address(), nil } func (m *Manager) GetChallenge(_ context.Context) ([]byte, uint16, error) { @@ -119,7 +117,7 @@ func (m *Manager) GetChallenge(_ context.Context) ([]byte, uint16, error) { return m.salt, m.difficulty, nil } -func (m *Manager) sendFunds(ctx context.Context, destination ed25519.PublicKey, amount uint64) (ids.ID, uint64, error) { +func (m *Manager) sendFunds(ctx context.Context, destination codec.Address, amount uint64) (ids.ID, uint64, error) { parser, err := m.tcli.Parser(ctx) if err != nil { return ids.Empty, 0, err @@ -136,8 +134,7 @@ func (m *Manager) sendFunds(ctx context.Context, destination ed25519.PublicKey, m.log.Warn("abandoning airdrop because network fee is greater than amount", zap.String("maxFee", utils.FormatBalance(maxFee, consts.Decimals))) return ids.Empty, 0, errors.New("network fee too high") } - addr := tutils.Address(m.config.PrivateKey().PublicKey()) - bal, err := m.tcli.Balance(ctx, addr, ids.Empty) + bal, err := m.tcli.Balance(ctx, m.config.AddressBech32(), ids.Empty) if err != nil { return ids.Empty, 0, err } @@ -149,7 +146,7 @@ func (m *Manager) sendFunds(ctx context.Context, destination ed25519.PublicKey, return tx.ID(), maxFee, submit(ctx) } -func (m *Manager) SolveChallenge(ctx context.Context, solver ed25519.PublicKey, salt []byte, solution []byte) (ids.ID, uint64, error) { +func (m *Manager) SolveChallenge(ctx context.Context, solver codec.Address, salt []byte, solution []byte) (ids.ID, uint64, error) { m.l.Lock() defer m.l.Unlock() @@ -173,7 +170,7 @@ func (m *Manager) SolveChallenge(ctx context.Context, solver ed25519.PublicKey, m.log.Info("fauceted funds", zap.Stringer("txID", txID), zap.String("max fee", utils.FormatBalance(maxFee, consts.Decimals)), - zap.String("destination", tutils.Address(solver)), + zap.String("destination", codec.MustAddressBech32(consts.HRP, solver)), zap.String("amount", utils.FormatBalance(m.config.Amount, consts.Decimals)), ) m.solutions.Add(solutionID) diff --git a/examples/tokenvm/cmd/token-faucet/rpc/dependencies.go b/examples/tokenvm/cmd/token-faucet/rpc/dependencies.go index f0e572393f..01cf9e474d 100644 --- a/examples/tokenvm/cmd/token-faucet/rpc/dependencies.go +++ b/examples/tokenvm/cmd/token-faucet/rpc/dependencies.go @@ -7,11 +7,11 @@ import ( "context" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" ) type Manager interface { - GetFaucetAddress(context.Context) (ed25519.PublicKey, error) + GetFaucetAddress(context.Context) (codec.Address, error) GetChallenge(context.Context) ([]byte, uint16, error) - SolveChallenge(context.Context, ed25519.PublicKey, []byte, []byte) (ids.ID, uint64, error) + SolveChallenge(context.Context, codec.Address, []byte, []byte) (ids.ID, uint64, error) } diff --git a/examples/tokenvm/cmd/token-faucet/rpc/jsonrpc_server.go b/examples/tokenvm/cmd/token-faucet/rpc/jsonrpc_server.go index c0d026f952..0cb8c4cbc9 100644 --- a/examples/tokenvm/cmd/token-faucet/rpc/jsonrpc_server.go +++ b/examples/tokenvm/cmd/token-faucet/rpc/jsonrpc_server.go @@ -8,7 +8,8 @@ import ( "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" + "github.com/ava-labs/hypersdk/codec" + "github.com/ava-labs/hypersdk/examples/tokenvm/consts" ) type JSONRPCServer struct { @@ -28,7 +29,7 @@ func (j *JSONRPCServer) FaucetAddress(req *http.Request, _ *struct{}, reply *Fau if err != nil { return err } - reply.Address = utils.Address(addr) + reply.Address = codec.MustAddressBech32(consts.HRP, addr) return nil } @@ -59,7 +60,7 @@ type SolveChallengeReply struct { } func (j *JSONRPCServer) SolveChallenge(req *http.Request, args *SolveChallengeArgs, reply *SolveChallengeReply) error { - addr, err := utils.ParseAddress(args.Address) + addr, err := codec.ParseAddressBech32(consts.HRP, args.Address) if err != nil { return err } diff --git a/examples/tokenvm/cmd/token-feed/config/config.go b/examples/tokenvm/cmd/token-feed/config/config.go index 83b00e5c14..7ff17ce4e5 100644 --- a/examples/tokenvm/cmd/token-feed/config/config.go +++ b/examples/tokenvm/cmd/token-feed/config/config.go @@ -4,8 +4,8 @@ package config import ( - "github.com/ava-labs/hypersdk/crypto/ed25519" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" + "github.com/ava-labs/hypersdk/codec" + "github.com/ava-labs/hypersdk/examples/tokenvm/consts" ) type Config struct { @@ -14,8 +14,8 @@ type Config struct { TokenRPC string `json:"tokenRPC"` - Recipient string `json:"recipient"` - recipientPublicKey ed25519.PublicKey + Recipient string `json:"recipient"` + recipientAddr codec.Address FeedSize int `json:"feedSize"` MinFee uint64 `json:"minFee"` @@ -24,13 +24,13 @@ type Config struct { TargetDurationPerEpoch int64 `json:"targetDurationPerEpoch"` // seconds } -func (c *Config) RecipientPublicKey() (ed25519.PublicKey, error) { - if c.recipientPublicKey != ed25519.EmptyPublicKey { - return c.recipientPublicKey, nil +func (c *Config) RecipientAddress() (codec.Address, error) { + if c.recipientAddr != codec.EmptyAddress { + return c.recipientAddr, nil } - pk, err := utils.ParseAddress(c.Recipient) + addr, err := codec.ParseAddressBech32(consts.HRP, c.Recipient) if err == nil { - c.recipientPublicKey = pk + c.recipientAddr = addr } - return pk, err + return addr, err } diff --git a/examples/tokenvm/cmd/token-feed/main.go b/examples/tokenvm/cmd/token-feed/main.go index dd03db9d2a..87fc8a5466 100644 --- a/examples/tokenvm/cmd/token-feed/main.go +++ b/examples/tokenvm/cmd/token-feed/main.go @@ -67,7 +67,7 @@ func main() { } // Load recipient - if _, err := c.RecipientPublicKey(); err != nil { + if _, err := c.RecipientAddress(); err != nil { fatal(log, "cannot parse recipient address", zap.Error(err)) } log.Info("loaded feed recipient", zap.String("address", c.Recipient)) diff --git a/examples/tokenvm/cmd/token-feed/manager/manager.go b/examples/tokenvm/cmd/token-feed/manager/manager.go index c6c0be51c8..562271e0e4 100644 --- a/examples/tokenvm/cmd/token-feed/manager/manager.go +++ b/examples/tokenvm/cmd/token-feed/manager/manager.go @@ -12,13 +12,11 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/timer" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/examples/tokenvm/actions" - "github.com/ava-labs/hypersdk/examples/tokenvm/auth" "github.com/ava-labs/hypersdk/examples/tokenvm/cmd/token-feed/config" "github.com/ava-labs/hypersdk/examples/tokenvm/consts" trpc "github.com/ava-labs/hypersdk/examples/tokenvm/rpc" - tutils "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/pubsub" "github.com/ava-labs/hypersdk/rpc" "github.com/ava-labs/hypersdk/utils" @@ -107,7 +105,7 @@ func (m *Manager) Run(ctx context.Context) error { if err != nil { return err } - recipientPubKey, err := m.config.RecipientPublicKey() + recipientAddr, err := m.config.RecipientAddress() if err != nil { return err } @@ -137,38 +135,38 @@ func (m *Manager) Run(ctx context.Context) error { if !ok { continue } - if action.To != recipientPubKey { + if action.To != recipientAddr { continue } if len(action.Memo) == 0 { continue } result := results[i] - from := auth.GetActor(tx.Auth) + from := tx.Auth.Actor() + fromStr := codec.MustAddressBech32(consts.HRP, from) if !result.Success { - m.log.Info("incoming message failed on-chain", zap.String("from", tutils.Address(from)), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value), zap.Uint64("required", m.feeAmount)) + m.log.Info("incoming message failed on-chain", zap.String("from", fromStr), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value), zap.Uint64("required", m.feeAmount)) continue } if action.Value < m.feeAmount { - m.log.Info("incoming message did not pay enough", zap.String("from", tutils.Address(from)), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value), zap.Uint64("required", m.feeAmount)) + m.log.Info("incoming message did not pay enough", zap.String("from", fromStr), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value), zap.Uint64("required", m.feeAmount)) continue } var c FeedContent if err := json.Unmarshal(action.Memo, &c); err != nil { - m.log.Info("incoming message could not be parsed", zap.String("from", tutils.Address(from)), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value), zap.Error(err)) + m.log.Info("incoming message could not be parsed", zap.String("from", fromStr), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value), zap.Error(err)) continue } if len(c.Message) == 0 { - m.log.Info("incoming message was empty", zap.String("from", tutils.Address(from)), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value)) + m.log.Info("incoming message was empty", zap.String("from", fromStr), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value)) continue } // TODO: pre-verify URLs - addr := tutils.Address(from) m.l.Lock() m.f.Lock() m.feed = append([]*FeedObject{{ - Address: addr, + Address: fromStr, TxID: tx.ID(), Timestamp: blk.Tmstmp, Fee: action.Value, @@ -188,7 +186,7 @@ func (m *Manager) Run(ctx context.Context) error { m.t.Cancel() m.t.SetTimeoutIn(time.Duration(m.config.TargetDurationPerEpoch) * time.Second) } - m.log.Info("received incoming message", zap.String("from", tutils.Address(from)), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value), zap.Uint64("new required", m.feeAmount)) + m.log.Info("received incoming message", zap.String("from", fromStr), zap.String("memo", string(action.Memo)), zap.Uint64("payment", action.Value), zap.Uint64("new required", m.feeAmount)) m.f.Unlock() m.l.Unlock() } @@ -203,12 +201,12 @@ func (m *Manager) Run(ctx context.Context) error { return ctx.Err() } -func (m *Manager) GetFeedInfo(_ context.Context) (ed25519.PublicKey, uint64, error) { +func (m *Manager) GetFeedInfo(_ context.Context) (codec.Address, uint64, error) { m.l.RLock() defer m.l.RUnlock() - pk, err := m.config.RecipientPublicKey() - return pk, m.feeAmount, err + addr, err := m.config.RecipientAddress() + return addr, m.feeAmount, err } // TODO: allow for multiple feeds diff --git a/examples/tokenvm/cmd/token-feed/rpc/dependencies.go b/examples/tokenvm/cmd/token-feed/rpc/dependencies.go index 823d1304b3..6008b779ce 100644 --- a/examples/tokenvm/cmd/token-feed/rpc/dependencies.go +++ b/examples/tokenvm/cmd/token-feed/rpc/dependencies.go @@ -6,11 +6,11 @@ package rpc import ( "context" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/examples/tokenvm/cmd/token-feed/manager" ) type Manager interface { - GetFeedInfo(context.Context) (ed25519.PublicKey, uint64, error) + GetFeedInfo(context.Context) (codec.Address, uint64, error) GetFeed(context.Context) ([]*manager.FeedObject, error) } diff --git a/examples/tokenvm/cmd/token-feed/rpc/jsonrpc_server.go b/examples/tokenvm/cmd/token-feed/rpc/jsonrpc_server.go index 04cf43f6b3..73e2d30f49 100644 --- a/examples/tokenvm/cmd/token-feed/rpc/jsonrpc_server.go +++ b/examples/tokenvm/cmd/token-feed/rpc/jsonrpc_server.go @@ -6,8 +6,9 @@ package rpc import ( "net/http" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/examples/tokenvm/cmd/token-feed/manager" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" + "github.com/ava-labs/hypersdk/examples/tokenvm/consts" ) type JSONRPCServer struct { @@ -28,7 +29,7 @@ func (j *JSONRPCServer) FeedInfo(req *http.Request, _ *struct{}, reply *FeedInfo if err != nil { return err } - reply.Address = utils.Address(addr) + reply.Address = codec.MustAddressBech32(consts.HRP, addr) reply.Fee = fee return nil } diff --git a/examples/tokenvm/cmd/token-wallet/backend/backend.go b/examples/tokenvm/cmd/token-wallet/backend/backend.go index 244e0f68d6..3d29db9cb8 100644 --- a/examples/tokenvm/cmd/token-wallet/backend/backend.go +++ b/examples/tokenvm/cmd/token-wallet/backend/backend.go @@ -26,6 +26,7 @@ import ( "github.com/ava-labs/avalanchego/utils/units" "github.com/ava-labs/hypersdk/chain" hcli "github.com/ava-labs/hypersdk/cli" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/examples/tokenvm/actions" @@ -36,7 +37,6 @@ import ( ferpc "github.com/ava-labs/hypersdk/examples/tokenvm/cmd/token-feed/rpc" tconsts "github.com/ava-labs/hypersdk/examples/tokenvm/consts" trpc "github.com/ava-labs/hypersdk/examples/tokenvm/rpc" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/pubsub" "github.com/ava-labs/hypersdk/rpc" hutils "github.com/ava-labs/hypersdk/utils" @@ -57,8 +57,8 @@ type Backend struct { priv ed25519.PrivateKey factory *auth.ED25519Factory - pk ed25519.PublicKey - addr string + addr codec.Address + addrStr string cli *rpc.JSONRPCClient chainID ids.ID @@ -131,9 +131,9 @@ func (b *Backend) Start(ctx context.Context) error { } b.priv = key b.factory = auth.NewED25519Factory(b.priv) - b.pk = b.priv.PublicKey() - b.addr = utils.Address(b.pk) - if err := b.AddAddressBook("Me", b.addr); err != nil { + b.addr = auth.NewED25519Address(b.priv.PublicKey()) + b.addrStr = codec.MustAddressBech32(tconsts.HRP, b.addr) + if err := b.AddAddressBook("Me", b.addrStr); err != nil { return err } if err := b.s.StoreAsset(ids.Empty, false); err != nil { @@ -214,7 +214,7 @@ func (b *Backend) collectBlocks() { consumed = nconsumed tx := blk.Txs[i] - actor := auth.GetActor(tx.Auth) + actor := tx.Auth.Actor() if !result.Success { failTxs++ } @@ -222,7 +222,7 @@ func (b *Backend) collectBlocks() { // We should exit action parsing as soon as possible switch action := tx.Action.(type) { case *actions.Transfer: - if actor != b.pk && action.To != b.pk { + if actor != b.addr && action.To != b.addr { continue } @@ -236,21 +236,21 @@ func (b *Backend) collectBlocks() { Size: fmt.Sprintf("%.2fKB", float64(tx.Size())/units.KiB), Success: result.Success, Timestamp: blk.Tmstmp, - Actor: utils.Address(actor), + Actor: codec.MustAddressBech32(tconsts.HRP, actor), Type: "Transfer", Units: hcli.ParseDimensions(result.Consumed), Fee: fmt.Sprintf("%s %s", hutils.FormatBalance(result.Fee, tconsts.Decimals), tconsts.Symbol), } if result.Success { - txInfo.Summary = fmt.Sprintf("%s %s -> %s", hutils.FormatBalance(action.Value, decimals), symbol, utils.Address(action.To)) + txInfo.Summary = fmt.Sprintf("%s %s -> %s", hutils.FormatBalance(action.Value, decimals), symbol, codec.MustAddressBech32(tconsts.HRP, action.To)) if len(action.Memo) > 0 { txInfo.Summary += fmt.Sprintf(" (memo: %s)", action.Memo) } } else { txInfo.Summary = string(result.Output) } - if action.To == b.pk { - if actor != b.pk && result.Success { + if action.To == b.addr { + if actor != b.addr && result.Success { b.txAlertLock.Lock() b.transactionAlerts = append(b.transactionAlerts, &Alert{"info", fmt.Sprintf("Received %s %s from Transfer", hutils.FormatBalance(action.Value, decimals), symbol)}) b.txAlertLock.Unlock() @@ -261,7 +261,7 @@ func (b *Backend) collectBlocks() { return } if !hasAsset { - if err := b.s.StoreAsset(action.Asset, b.addr == owner); err != nil { + if err := b.s.StoreAsset(action.Asset, b.addrStr == owner); err != nil { b.fatal(err) return } @@ -270,14 +270,14 @@ func (b *Backend) collectBlocks() { b.fatal(err) return } - } else if actor == b.pk { + } else if actor == b.addr { if err := b.s.StoreTransaction(txInfo); err != nil { b.fatal(err) return } } case *actions.CreateAsset: - if actor != b.pk { + if actor != b.addr { continue } @@ -290,7 +290,7 @@ func (b *Backend) collectBlocks() { Size: fmt.Sprintf("%.2fKB", float64(tx.Size())/units.KiB), Success: result.Success, Timestamp: blk.Tmstmp, - Actor: utils.Address(actor), + Actor: codec.MustAddressBech32(tconsts.HRP, actor), Type: "CreateAsset", Units: hcli.ParseDimensions(result.Consumed), Fee: fmt.Sprintf("%s %s", hutils.FormatBalance(result.Fee, tconsts.Decimals), tconsts.Symbol), @@ -305,7 +305,7 @@ func (b *Backend) collectBlocks() { return } case *actions.MintAsset: - if actor != b.pk && action.To != b.pk { + if actor != b.addr && action.To != b.addr { continue } @@ -319,18 +319,18 @@ func (b *Backend) collectBlocks() { Timestamp: blk.Tmstmp, Size: fmt.Sprintf("%.2fKB", float64(tx.Size())/units.KiB), Success: result.Success, - Actor: utils.Address(actor), + Actor: codec.MustAddressBech32(tconsts.HRP, actor), Type: "Mint", Units: hcli.ParseDimensions(result.Consumed), Fee: fmt.Sprintf("%s %s", hutils.FormatBalance(result.Fee, tconsts.Decimals), tconsts.Symbol), } if result.Success { - txInfo.Summary = fmt.Sprintf("%s %s -> %s", hutils.FormatBalance(action.Value, decimals), symbol, utils.Address(action.To)) + txInfo.Summary = fmt.Sprintf("%s %s -> %s", hutils.FormatBalance(action.Value, decimals), symbol, codec.MustAddressBech32(tconsts.HRP, action.To)) } else { txInfo.Summary = string(result.Output) } - if action.To == b.pk { - if actor != b.pk && result.Success { + if action.To == b.addr { + if actor != b.addr && result.Success { b.txAlertLock.Lock() b.transactionAlerts = append(b.transactionAlerts, &Alert{"info", fmt.Sprintf("Received %s %s from Mint", hutils.FormatBalance(action.Value, decimals), symbol)}) b.txAlertLock.Unlock() @@ -341,7 +341,7 @@ func (b *Backend) collectBlocks() { return } if !hasAsset { - if err := b.s.StoreAsset(action.Asset, b.addr == owner); err != nil { + if err := b.s.StoreAsset(action.Asset, b.addrStr == owner); err != nil { b.fatal(err) return } @@ -350,14 +350,14 @@ func (b *Backend) collectBlocks() { b.fatal(err) return } - } else if actor == b.pk { + } else if actor == b.addr { if err := b.s.StoreTransaction(txInfo); err != nil { b.fatal(err) return } } case *actions.CreateOrder: - if actor != b.pk { + if actor != b.addr { continue } @@ -376,7 +376,7 @@ func (b *Backend) collectBlocks() { Timestamp: blk.Tmstmp, Size: fmt.Sprintf("%.2fKB", float64(tx.Size())/units.KiB), Success: result.Success, - Actor: utils.Address(actor), + Actor: codec.MustAddressBech32(tconsts.HRP, actor), Type: "CreateOrder", Units: hcli.ParseDimensions(result.Consumed), Fee: fmt.Sprintf("%s %s", hutils.FormatBalance(result.Fee, tconsts.Decimals), tconsts.Symbol), @@ -398,7 +398,7 @@ func (b *Backend) collectBlocks() { return } case *actions.FillOrder: - if actor != b.pk && action.Owner != b.pk { + if actor != b.addr && action.Owner != b.addr { continue } @@ -417,7 +417,7 @@ func (b *Backend) collectBlocks() { Timestamp: blk.Tmstmp, Size: fmt.Sprintf("%.2fKB", float64(tx.Size())/units.KiB), Success: result.Success, - Actor: utils.Address(actor), + Actor: codec.MustAddressBech32(tconsts.HRP, actor), Type: "FillOrder", Units: hcli.ParseDimensions(result.Consumed), Fee: fmt.Sprintf("%s %s", hutils.FormatBalance(result.Fee, tconsts.Decimals), tconsts.Symbol), @@ -433,7 +433,7 @@ func (b *Backend) collectBlocks() { outSymbol, ) - if action.Owner == b.pk && actor != b.pk { + if action.Owner == b.addr && actor != b.addr { b.txAlertLock.Lock() b.transactionAlerts = append(b.transactionAlerts, &Alert{"info", fmt.Sprintf("Received %s %s from FillOrder", hutils.FormatBalance(or.In, inDecimals), inSymbol)}) b.txAlertLock.Unlock() @@ -441,14 +441,14 @@ func (b *Backend) collectBlocks() { } else { txInfo.Summary = string(result.Output) } - if actor == b.pk { + if actor == b.addr { if err := b.s.StoreTransaction(txInfo); err != nil { b.fatal(err) return } } case *actions.CloseOrder: - if actor != b.pk { + if actor != b.addr { continue } @@ -457,7 +457,7 @@ func (b *Backend) collectBlocks() { Timestamp: blk.Tmstmp, Size: fmt.Sprintf("%.2fKB", float64(tx.Size())/units.KiB), Success: result.Success, - Actor: utils.Address(actor), + Actor: codec.MustAddressBech32(tconsts.HRP, actor), Type: "CloseOrder", Units: hcli.ParseDimensions(result.Consumed), Fee: fmt.Sprintf("%s %s", hutils.FormatBalance(result.Fee, tconsts.Decimals), tconsts.Symbol), @@ -526,7 +526,7 @@ func (b *Backend) collectBlocks() { } b.currentStat.Transactions += bi.Txs for _, tx := range blk.Txs { - b.currentStat.Accounts.Add(string(tx.Auth.Payer())) + b.currentStat.Accounts.Add(codec.MustAddressBech32(tconsts.HRP, tx.Auth.Sponsor())) } b.currentStat.Prices = prices snow := time.Now().Unix() @@ -629,7 +629,7 @@ func (b *Backend) GetMyAssets() []*AssetInfo { func (b *Backend) CreateAsset(symbol string, decimals string, metadata string) error { // Ensure have sufficient balance - bal, err := b.tcli.Balance(b.ctx, b.addr, ids.Empty) + bal, err := b.tcli.Balance(b.ctx, b.addrStr, ids.Empty) if err != nil { return err } @@ -682,13 +682,13 @@ func (b *Backend) MintAsset(asset string, address string, amount string) error { if err != nil { return err } - to, err := utils.ParseAddress(address) + to, err := codec.ParseAddressBech32(tconsts.HRP, address) if err != nil { return err } // Ensure have sufficient balance - bal, err := b.tcli.Balance(b.ctx, b.addr, ids.Empty) + bal, err := b.tcli.Balance(b.ctx, b.addrStr, ids.Empty) if err != nil { return err } @@ -737,13 +737,13 @@ func (b *Backend) Transfer(asset string, address string, amount string, memo str if err != nil { return err } - to, err := utils.ParseAddress(address) + to, err := codec.ParseAddressBech32(tconsts.HRP, address) if err != nil { return err } // Ensure have sufficient balance for transfer - sendBal, err := b.tcli.Balance(b.ctx, b.addr, assetID) + sendBal, err := b.tcli.Balance(b.ctx, b.addrStr, assetID) if err != nil { return err } @@ -752,7 +752,7 @@ func (b *Backend) Transfer(asset string, address string, amount string, memo str } // Ensure have sufficient balance for fees - bal, err := b.tcli.Balance(b.ctx, b.addr, ids.Empty) + bal, err := b.tcli.Balance(b.ctx, b.addrStr, ids.Empty) if err != nil { return err } @@ -795,7 +795,7 @@ func (b *Backend) Transfer(asset string, address string, amount string, memo str } func (b *Backend) GetAddress() string { - return b.addr + return b.addrStr } func (b *Backend) GetBalance() ([]*BalanceInfo, error) { @@ -809,7 +809,7 @@ func (b *Backend) GetBalance() ([]*BalanceInfo, error) { if err != nil { return nil, err } - bal, err := b.tcli.Balance(b.ctx, b.addr, asset) + bal, err := b.tcli.Balance(b.ctx, b.addrStr, asset) if err != nil { return nil, err } @@ -871,7 +871,7 @@ func (b *Backend) StartFaucetSearch() (*FaucetSearchInfo, error) { go func() { start := time.Now() solution, attempts := challenge.Search(salt, difficulty, b.c.SearchCores) - txID, amount, err := b.fcli.SolveChallenge(b.ctx, b.addr, salt, solution) + txID, amount, err := b.fcli.SolveChallenge(b.ctx, b.addrStr, salt, solution) b.searchLock.Lock() b.search.Solution = hex.EncodeToString(solution) b.search.Attempts = attempts @@ -975,7 +975,7 @@ func (b *Backend) AddAsset(asset string) error { if !exists { return ErrAssetMissing } - return b.s.StoreAsset(assetID, owner == b.addr) + return b.s.StoreAsset(assetID, owner == b.addrStr) } func (b *Backend) GetMyOrders() ([]*Order, error) { @@ -1090,11 +1090,11 @@ func (b *Backend) CreateOrder(assetIn string, inTick string, assetOut string, ou } // Ensure have sufficient balance - bal, err := b.tcli.Balance(b.ctx, b.addr, ids.Empty) + bal, err := b.tcli.Balance(b.ctx, b.addrStr, ids.Empty) if err != nil { return err } - outBal, err := b.tcli.Balance(b.ctx, b.addr, outID) + outBal, err := b.tcli.Balance(b.ctx, b.addrStr, outID) if err != nil { return err } @@ -1159,7 +1159,7 @@ func (b *Backend) FillOrder(orderID string, orderOwner string, assetIn string, i if err != nil { return err } - owner, err := utils.ParseAddress(orderOwner) + owner, err := codec.ParseAddressBech32(tconsts.HRP, orderOwner) if err != nil { return err } @@ -1177,11 +1177,11 @@ func (b *Backend) FillOrder(orderID string, orderOwner string, assetIn string, i } // Ensure have sufficient balance - bal, err := b.tcli.Balance(b.ctx, b.addr, ids.Empty) + bal, err := b.tcli.Balance(b.ctx, b.addrStr, ids.Empty) if err != nil { return err } - inBal, err := b.tcli.Balance(b.ctx, b.addr, inID) + inBal, err := b.tcli.Balance(b.ctx, b.addrStr, inID) if err != nil { return err } @@ -1249,7 +1249,7 @@ func (b *Backend) CloseOrder(orderID string, assetOut string) error { } // Ensure have sufficient balance - bal, err := b.tcli.Balance(b.ctx, b.addr, ids.Empty) + bal, err := b.tcli.Balance(b.ctx, b.addrStr, ids.Empty) if err != nil { return err } @@ -1373,7 +1373,7 @@ func (b *Backend) Message(message string, url string) error { if err != nil { return err } - recipientAddr, err := utils.ParseAddress(recipient) + recipientAddr, err := codec.ParseAddressBech32(tconsts.HRP, recipient) if err != nil { return err } @@ -1389,7 +1389,7 @@ func (b *Backend) Message(message string, url string) error { } // Ensure have sufficient balance - bal, err := b.tcli.Balance(b.ctx, b.addr, ids.Empty) + bal, err := b.tcli.Balance(b.ctx, b.addrStr, ids.Empty) if err != nil { return err } diff --git a/examples/tokenvm/cmd/token-wallet/backend/storage.go b/examples/tokenvm/cmd/token-wallet/backend/storage.go index 6da5704cc3..9712584369 100644 --- a/examples/tokenvm/cmd/token-wallet/backend/storage.go +++ b/examples/tokenvm/cmd/token-wallet/backend/storage.go @@ -12,10 +12,10 @@ import ( "github.com/ava-labs/avalanchego/database" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" "github.com/ava-labs/hypersdk/crypto/ed25519" tconsts "github.com/ava-labs/hypersdk/examples/tokenvm/consts" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/pebble" hutils "github.com/ava-labs/hypersdk/utils" ) @@ -127,13 +127,13 @@ func (s *Storage) GetTransactions() ([]*TransactionInfo, error) { } func (s *Storage) StoreAddress(address string, nickname string) error { - pk, err := utils.ParseAddress(address) + addr, err := codec.ParseAddressBech32(tconsts.HRP, address) if err != nil { return err } - k := make([]byte, 1+ed25519.PublicKeyLen) + k := make([]byte, 1+codec.AddressLen) k[0] = addressPrefix - copy(k[1:], pk[:]) + copy(k[1:], addr[:]) return s.db.Put(k, []byte(nickname)) } @@ -143,10 +143,9 @@ func (s *Storage) GetAddresses() ([]*AddressInfo, error) { addresses := []*AddressInfo{} for iter.Next() { - pk := ed25519.PublicKey(iter.Key()[1:]) - address := utils.Address(pk) + address := codec.Address(iter.Key()[1:]) nickname := string(iter.Value()) - addresses = append(addresses, &AddressInfo{nickname, address, fmt.Sprintf("%s [%s..%s]", nickname, address[:len(tconsts.HRP)+3], address[len(address)-3:])}) + addresses = append(addresses, &AddressInfo{nickname, codec.MustAddressBech32(tconsts.HRP, address), fmt.Sprintf("%s [%s..%s]", nickname, address[:len(tconsts.HRP)+3], address[len(address)-3:])}) } return addresses, iter.Error() } diff --git a/examples/tokenvm/config/config.go b/examples/tokenvm/config/config.go index 92098a9cc0..8c0b7f35e3 100644 --- a/examples/tokenvm/config/config.go +++ b/examples/tokenvm/config/config.go @@ -12,13 +12,13 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/avalanchego/utils/profiler" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/config" "github.com/ava-labs/hypersdk/gossiper" "github.com/ava-labs/hypersdk/trace" "github.com/ava-labs/hypersdk/vm" "github.com/ava-labs/hypersdk/examples/tokenvm/consts" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/examples/tokenvm/version" ) @@ -57,9 +57,9 @@ type Config struct { StreamingBacklogSize int `json:"streamingBacklogSize"` // Mempool - MempoolSize int `json:"mempoolSize"` - MempoolPayerSize int `json:"mempoolPayerSize"` - MempoolExemptPayers []string `json:"mempoolExemptPayers"` + MempoolSize int `json:"mempoolSize"` + MempoolSponsorSize int `json:"mempoolSponsorSize"` + MempoolExemptSponsors []string `json:"mempoolExemptSponsors"` // Order Book // @@ -76,9 +76,9 @@ type Config struct { // State Sync StateSyncServerDelay time.Duration `json:"stateSyncServerDelay"` // for testing - loaded bool - nodeID ids.NodeID - parsedExemptPayers [][]byte + loaded bool + nodeID ids.NodeID + parsedExemptSponsors []codec.Address } func New(nodeID ids.NodeID, b []byte) (*Config, error) { @@ -91,15 +91,15 @@ func New(nodeID ids.NodeID, b []byte) (*Config, error) { c.loaded = true } - // Parse any exempt payers (usually used when a single account is + // Parse any exempt sponsors (usually used when a single account is // broadcasting many txs at once) - c.parsedExemptPayers = make([][]byte, len(c.MempoolExemptPayers)) - for i, payer := range c.MempoolExemptPayers { - p, err := utils.ParseAddress(payer) + c.parsedExemptSponsors = make([]codec.Address, len(c.MempoolExemptSponsors)) + for i, sponsor := range c.MempoolExemptSponsors { + p, err := codec.ParseAddressBech32(consts.HRP, sponsor) if err != nil { return nil, err } - c.parsedExemptPayers[i] = p[:] + c.parsedExemptSponsors[i] = p } return c, nil } @@ -116,7 +116,7 @@ func (c *Config) setDefault() { c.RootGenerationCores = c.Config.GetRootGenerationCores() c.TransactionExecutionCores = c.Config.GetTransactionExecutionCores() c.MempoolSize = c.Config.GetMempoolSize() - c.MempoolPayerSize = c.Config.GetMempoolPayerSize() + c.MempoolSponsorSize = c.Config.GetMempoolSponsorSize() c.StateSyncServerDelay = c.Config.GetStateSyncServerDelay() c.StreamingBacklogSize = c.Config.GetStreamingBacklogSize() c.VerifySignatures = c.Config.GetVerifySignatures() @@ -124,14 +124,14 @@ func (c *Config) setDefault() { c.MaxOrdersPerPair = defaultMaxOrdersPerPair } -func (c *Config) GetLogLevel() logging.Level { return c.LogLevel } -func (c *Config) GetTestMode() bool { return c.TestMode } -func (c *Config) GetSignatureVerificationCores() int { return c.SignatureVerificationCores } -func (c *Config) GetRootGenerationCores() int { return c.RootGenerationCores } -func (c *Config) GetTransactionExecutionCores() int { return c.TransactionExecutionCores } -func (c *Config) GetMempoolSize() int { return c.MempoolSize } -func (c *Config) GetMempoolPayerSize() int { return c.MempoolPayerSize } -func (c *Config) GetMempoolExemptPayers() [][]byte { return c.parsedExemptPayers } +func (c *Config) GetLogLevel() logging.Level { return c.LogLevel } +func (c *Config) GetTestMode() bool { return c.TestMode } +func (c *Config) GetSignatureVerificationCores() int { return c.SignatureVerificationCores } +func (c *Config) GetRootGenerationCores() int { return c.RootGenerationCores } +func (c *Config) GetTransactionExecutionCores() int { return c.TransactionExecutionCores } +func (c *Config) GetMempoolSize() int { return c.MempoolSize } +func (c *Config) GetMempoolSponsorSize() int { return c.MempoolSponsorSize } +func (c *Config) GetMempoolExemptSponsors() []codec.Address { return c.parsedExemptSponsors } func (c *Config) GetTraceConfig() *trace.Config { return &trace.Config{ Enabled: c.TraceEnabled, diff --git a/examples/tokenvm/controller/controller.go b/examples/tokenvm/controller/controller.go index 53331c1991..5e3e7fc436 100644 --- a/examples/tokenvm/controller/controller.go +++ b/examples/tokenvm/controller/controller.go @@ -191,8 +191,7 @@ func (c *Controller) Accepted(ctx context.Context, blk *chain.StatelessBlock) er c.metrics.transfer.Inc() case *actions.CreateOrder: c.metrics.createOrder.Inc() - actor := auth.GetActor(tx.Auth) - c.orderBook.Add(tx.ID(), actor, action) + c.orderBook.Add(tx.ID(), tx.Auth.Actor(), action) case *actions.FillOrder: c.metrics.fillOrder.Inc() orderResult, err := actions.UnmarshalOrderResult(result.Output) diff --git a/examples/tokenvm/controller/resolutions.go b/examples/tokenvm/controller/resolutions.go index 57a57dd073..7a2597f4bc 100644 --- a/examples/tokenvm/controller/resolutions.go +++ b/examples/tokenvm/controller/resolutions.go @@ -10,7 +10,7 @@ import ( "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/avalanchego/utils/logging" "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/examples/tokenvm/genesis" "github.com/ava-labs/hypersdk/examples/tokenvm/orderbook" "github.com/ava-labs/hypersdk/examples/tokenvm/storage" @@ -38,16 +38,16 @@ func (c *Controller) GetTransaction( func (c *Controller) GetAssetFromState( ctx context.Context, asset ids.ID, -) (bool, []byte, uint8, []byte, uint64, ed25519.PublicKey, bool, error) { +) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) { return storage.GetAssetFromState(ctx, c.inner.ReadState, asset) } func (c *Controller) GetBalanceFromState( ctx context.Context, - pk ed25519.PublicKey, + addr codec.Address, asset ids.ID, ) (uint64, error) { - return storage.GetBalanceFromState(ctx, c.inner.ReadState, pk, asset) + return storage.GetBalanceFromState(ctx, c.inner.ReadState, addr, asset) } func (c *Controller) Orders(pair string, limit int) []*orderbook.Order { @@ -64,7 +64,7 @@ func (c *Controller) GetOrderFromState( ids.ID, // out uint64, // outTick uint64, // remaining - ed25519.PublicKey, // owner + codec.Address, // owner error, ) { return storage.GetOrderFromState(ctx, c.inner.ReadState, orderID) diff --git a/examples/tokenvm/genesis/genesis.go b/examples/tokenvm/genesis/genesis.go index b8b46e5927..7937cc3fc3 100644 --- a/examples/tokenvm/genesis/genesis.go +++ b/examples/tokenvm/genesis/genesis.go @@ -14,11 +14,10 @@ import ( "github.com/ava-labs/avalanchego/x/merkledb" "github.com/ava-labs/hypersdk/chain" + "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/consts" "github.com/ava-labs/hypersdk/examples/tokenvm/storage" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/state" "github.com/ava-labs/hypersdk/vm" ) @@ -31,8 +30,6 @@ type CustomAllocation struct { } type Genesis struct { - HRP string `json:"hrp"` - // State Parameters StateBranchFactor merkledb.BranchFactor `json:"stateBranchFactor"` @@ -71,8 +68,6 @@ type Genesis struct { func Default() *Genesis { return &Genesis{ - HRP: consts.HRP, - // State Parameters StateBranchFactor: merkledb.BranchFactor16, @@ -125,16 +120,13 @@ func (g *Genesis) Load(ctx context.Context, tracer trace.Tracer, mu state.Mutabl ctx, span := tracer.Start(ctx, "Genesis.Load") defer span.End() - if consts.HRP != g.HRP { - return ErrInvalidHRP - } if err := g.StateBranchFactor.Valid(); err != nil { return err } supply := uint64(0) for _, alloc := range g.CustomAllocation { - pk, err := utils.ParseAddress(alloc.Address) + pk, err := codec.ParseAddressBech32(consts.HRP, alloc.Address) if err != nil { return err } @@ -154,7 +146,7 @@ func (g *Genesis) Load(ctx context.Context, tracer trace.Tracer, mu state.Mutabl consts.Decimals, []byte(consts.Name), supply, - ed25519.EmptyPublicKey, + codec.EmptyAddress, false, ) } diff --git a/examples/tokenvm/orderbook/orderbook.go b/examples/tokenvm/orderbook/orderbook.go index 0efa7373f7..28f518c9a2 100644 --- a/examples/tokenvm/orderbook/orderbook.go +++ b/examples/tokenvm/orderbook/orderbook.go @@ -7,9 +7,9 @@ import ( "sync" "github.com/ava-labs/avalanchego/ids" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/examples/tokenvm/actions" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" + "github.com/ava-labs/hypersdk/examples/tokenvm/consts" "github.com/ava-labs/hypersdk/heap" "go.uber.org/zap" ) @@ -25,7 +25,7 @@ type Order struct { OutTick uint64 `json:"outTick"` Remaining uint64 `json:"remaining"` - owner ed25519.PublicKey + owner codec.Address } type OrderBook struct { @@ -65,11 +65,11 @@ func New(c Controller, trackedPairs []string, maxOrdersPerPair int) *OrderBook { } } -func (o *OrderBook) Add(txID ids.ID, actor ed25519.PublicKey, action *actions.CreateOrder) { +func (o *OrderBook) Add(txID ids.ID, actor codec.Address, action *actions.CreateOrder) { pair := actions.PairID(action.In, action.Out) order := &Order{ txID, - utils.Address(actor), + codec.MustAddressBech32(consts.HRP, actor), action.In, action.InTick, action.Out, diff --git a/examples/tokenvm/rpc/dependencies.go b/examples/tokenvm/rpc/dependencies.go index c199f9eace..65fce7ebf0 100644 --- a/examples/tokenvm/rpc/dependencies.go +++ b/examples/tokenvm/rpc/dependencies.go @@ -9,7 +9,7 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/hypersdk/chain" - "github.com/ava-labs/hypersdk/crypto/ed25519" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/examples/tokenvm/genesis" "github.com/ava-labs/hypersdk/examples/tokenvm/orderbook" ) @@ -18,8 +18,8 @@ type Controller interface { Genesis() *genesis.Genesis Tracer() trace.Tracer GetTransaction(context.Context, ids.ID) (bool, int64, bool, chain.Dimensions, uint64, error) - GetAssetFromState(context.Context, ids.ID) (bool, []byte, uint8, []byte, uint64, ed25519.PublicKey, bool, error) - GetBalanceFromState(context.Context, ed25519.PublicKey, ids.ID) (uint64, error) + GetAssetFromState(context.Context, ids.ID) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) + GetBalanceFromState(context.Context, codec.Address, ids.ID) (uint64, error) Orders(pair string, limit int) []*orderbook.Order GetOrderFromState(context.Context, ids.ID) ( bool, // exists @@ -28,7 +28,7 @@ type Controller interface { ids.ID, // out uint64, // outTick uint64, // remaining - ed25519.PublicKey, // owner + codec.Address, // owner error, ) GetLoanFromState(context.Context, ids.ID, ids.ID) (uint64, error) diff --git a/examples/tokenvm/rpc/jsonrpc_server.go b/examples/tokenvm/rpc/jsonrpc_server.go index 29e3c97fd8..46d77ef067 100644 --- a/examples/tokenvm/rpc/jsonrpc_server.go +++ b/examples/tokenvm/rpc/jsonrpc_server.go @@ -9,9 +9,10 @@ import ( "github.com/ava-labs/avalanchego/ids" "github.com/ava-labs/hypersdk/chain" + "github.com/ava-labs/hypersdk/codec" + "github.com/ava-labs/hypersdk/examples/tokenvm/consts" "github.com/ava-labs/hypersdk/examples/tokenvm/genesis" "github.com/ava-labs/hypersdk/examples/tokenvm/orderbook" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" ) type JSONRPCServer struct { @@ -88,7 +89,7 @@ func (j *JSONRPCServer) Asset(req *http.Request, args *AssetArgs, reply *AssetRe reply.Decimals = decimals reply.Metadata = metadata reply.Supply = supply - reply.Owner = utils.Address(owner) + reply.Owner = codec.MustAddressBech32(consts.HRP, owner) reply.Warp = warp return err } @@ -106,7 +107,7 @@ func (j *JSONRPCServer) Balance(req *http.Request, args *BalanceArgs, reply *Bal ctx, span := j.c.Tracer().Start(req.Context(), "Server.Balance") defer span.End() - addr, err := utils.ParseAddress(args.Address) + addr, err := codec.ParseAddressBech32(consts.HRP, args.Address) if err != nil { return err } @@ -155,7 +156,7 @@ func (j *JSONRPCServer) GetOrder(req *http.Request, args *GetOrderArgs, reply *G } reply.Order = &orderbook.Order{ ID: args.OrderID, - Owner: utils.Address(owner), + Owner: codec.MustAddressBech32(consts.HRP, owner), InAsset: in, InTick: inTick, OutAsset: out, diff --git a/examples/tokenvm/scripts/run.sh b/examples/tokenvm/scripts/run.sh index 9bc09f8ebf..40513bbec0 100755 --- a/examples/tokenvm/scripts/run.sh +++ b/examples/tokenvm/scripts/run.sh @@ -20,11 +20,13 @@ fi VERSION=v1.10.12 MAX_UINT64=18446744073709551615 MODE=${MODE:-run} +AGO_LOGLEVEL=${AGO_LOGLEVEL:-info} LOGLEVEL=${LOGLEVEL:-info} STATESYNC_DELAY=${STATESYNC_DELAY:-0} MIN_BLOCK_GAP=${MIN_BLOCK_GAP:-100} STORE_TXS=${STORE_TXS:-false} UNLIMITED_USAGE=${UNLIMITED_USAGE:-false} +ADDRESS=${ADDRESS:-token1qrzvk4zlwj9zsacqgtufx7zvapd3quufqpxk5rsdd4633m4wz2fdj73w34s} if [[ ${MODE} != "run" && ${MODE} != "run-single" ]]; then LOGLEVEL=debug STATESYNC_DELAY=100000000 # 100ms @@ -42,6 +44,7 @@ if ${UNLIMITED_USAGE}; then fi echo "Running with:" +echo AGO_LOGLEVEL: ${AGO_LOGLEVEL} echo LOGLEVEL: ${LOGLEVEL} echo VERSION: ${VERSION} echo MODE: ${MODE} @@ -50,6 +53,7 @@ echo MIN_BLOCK_GAP \(ms\): ${MIN_BLOCK_GAP} echo STORE_TXS: ${STORE_TXS} echo WINDOW_TARGET_UNITS: ${WINDOW_TARGET_UNITS} echo MAX_BLOCK_UNITS: ${MAX_BLOCK_UNITS} +echo ADDRESS: ${ADDRESS} ############################ # build avalanchego @@ -118,7 +122,7 @@ find ${TMPDIR}/avalanchego-${VERSION} # funds using the included demo.pk) echo "creating allocations file" cat < ${TMPDIR}/allocations.json -[{"address":"token1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsjzf3yp", "balance":10000000000000000000}] +[{"address":"${ADDRESS}", "balance":10000000000000000000}] EOF GENESIS_PATH=$2 @@ -149,8 +153,8 @@ rm -rf ${TMPDIR}/tokenvm-e2e-profiles cat < ${TMPDIR}/tokenvm.config { "mempoolSize": 10000000, - "mempoolPayerSize": 10000000, - "mempoolExemptPayers":["token1rvzhmceq997zntgvravfagsks6w0ryud3rylh4cdvayry0dl97nsjzf3yp"], + "mempoolSponsorSize": 10000000, + "mempoolExemptSponsors":["${ADDRESS}"], "signatureVerificationCores": 2, "rootGenerationCores": 2, "transactionExecutionCores": 2, @@ -200,7 +204,7 @@ ACK_GINKGO_RC=true ginkgo build ./tests/e2e # download avalanche-network-runner # https://github.com/ava-labs/avalanche-network-runner ANR_REPO_PATH=github.com/ava-labs/avalanche-network-runner -ANR_VERSION=v1.7.2 +ANR_VERSION=a641238e9a15a2ce4d9903c266307b93926d3733 # version set go install -v ${ANR_REPO_PATH}@${ANR_VERSION} @@ -250,7 +254,7 @@ echo "running e2e tests" ./tests/e2e/e2e.test \ --ginkgo.v \ --network-runner-log-level verbo \ ---avalanchego-log-level ${LOGLEVEL} \ +--avalanchego-log-level ${AGO_LOGLEVEL} \ --network-runner-grpc-endpoint="0.0.0.0:12352" \ --network-runner-grpc-gateway-endpoint="0.0.0.0:12353" \ --avalanchego-path=${AVALANCHEGO_PATH} \ diff --git a/examples/tokenvm/storage/storage.go b/examples/tokenvm/storage/storage.go index ae4e2da0df..e17d123041 100644 --- a/examples/tokenvm/storage/storage.go +++ b/examples/tokenvm/storage/storage.go @@ -14,11 +14,10 @@ import ( "github.com/ava-labs/avalanchego/ids" smath "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/hypersdk/chain" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/consts" - "github.com/ava-labs/hypersdk/crypto/ed25519" + tconsts "github.com/ava-labs/hypersdk/examples/tokenvm/consts" "github.com/ava-labs/hypersdk/state" - - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" ) type ReadState func(context.Context, [][]byte) ([][]byte, []error) @@ -74,7 +73,7 @@ var ( balanceKeyPool = sync.Pool{ New: func() any { - return make([]byte, 1+ed25519.PublicKeyLen+consts.IDLen+consts.Uint16Len) + return make([]byte, 1+codec.AddressLen+consts.IDLen+consts.Uint16Len) }, } ) @@ -136,12 +135,12 @@ func GetTransaction( } // [accountPrefix] + [address] + [asset] -func BalanceKey(pk ed25519.PublicKey, asset ids.ID) (k []byte) { +func BalanceKey(addr codec.Address, asset ids.ID) (k []byte) { k = balanceKeyPool.Get().([]byte) k[0] = balancePrefix - copy(k[1:], pk[:]) - copy(k[1+ed25519.PublicKeyLen:], asset[:]) - binary.BigEndian.PutUint16(k[1+ed25519.PublicKeyLen+consts.IDLen:], BalanceChunks) + copy(k[1:], addr[:]) + copy(k[1+codec.AddressLen:], asset[:]) + binary.BigEndian.PutUint16(k[1+codec.AddressLen+consts.IDLen:], BalanceChunks) return } @@ -149,10 +148,10 @@ func BalanceKey(pk ed25519.PublicKey, asset ids.ID) (k []byte) { func GetBalance( ctx context.Context, im state.Immutable, - pk ed25519.PublicKey, + addr codec.Address, asset ids.ID, ) (uint64, error) { - key, bal, _, err := getBalance(ctx, im, pk, asset) + key, bal, _, err := getBalance(ctx, im, addr, asset) balanceKeyPool.Put(key) return bal, err } @@ -160,10 +159,10 @@ func GetBalance( func getBalance( ctx context.Context, im state.Immutable, - pk ed25519.PublicKey, + addr codec.Address, asset ids.ID, ) ([]byte, uint64, bool, error) { - k := BalanceKey(pk, asset) + k := BalanceKey(addr, asset) bal, exists, err := innerGetBalance(im.GetValue(ctx, k)) return k, bal, exists, err } @@ -172,10 +171,10 @@ func getBalance( func GetBalanceFromState( ctx context.Context, f ReadState, - pk ed25519.PublicKey, + addr codec.Address, asset ids.ID, ) (uint64, error) { - k := BalanceKey(pk, asset) + k := BalanceKey(addr, asset) values, errs := f(ctx, [][]byte{k}) bal, _, err := innerGetBalance(values[0], errs[0]) balanceKeyPool.Put(k) @@ -198,11 +197,11 @@ func innerGetBalance( func SetBalance( ctx context.Context, mu state.Mutable, - pk ed25519.PublicKey, + addr codec.Address, asset ids.ID, balance uint64, ) error { - k := BalanceKey(pk, asset) + k := BalanceKey(addr, asset) return setBalance(ctx, mu, k, balance) } @@ -218,21 +217,21 @@ func setBalance( func DeleteBalance( ctx context.Context, mu state.Mutable, - pk ed25519.PublicKey, + addr codec.Address, asset ids.ID, ) error { - return mu.Remove(ctx, BalanceKey(pk, asset)) + return mu.Remove(ctx, BalanceKey(addr, asset)) } func AddBalance( ctx context.Context, mu state.Mutable, - pk ed25519.PublicKey, + addr codec.Address, asset ids.ID, amount uint64, create bool, ) error { - key, bal, exists, err := getBalance(ctx, mu, pk, asset) + key, bal, exists, err := getBalance(ctx, mu, addr, asset) if err != nil { return err } @@ -248,7 +247,7 @@ func AddBalance( ErrInvalidBalance, asset, bal, - utils.Address(pk), + codec.MustAddressBech32(tconsts.HRP, addr), amount, ) } @@ -258,11 +257,11 @@ func AddBalance( func SubBalance( ctx context.Context, mu state.Mutable, - pk ed25519.PublicKey, + addr codec.Address, asset ids.ID, amount uint64, ) error { - key, bal, _, err := getBalance(ctx, mu, pk, asset) + key, bal, _, err := getBalance(ctx, mu, addr, asset) if err != nil { return err } @@ -273,7 +272,7 @@ func SubBalance( ErrInvalidBalance, asset, bal, - utils.Address(pk), + codec.MustAddressBech32(tconsts.HRP, addr), amount, ) } @@ -299,7 +298,7 @@ func GetAssetFromState( ctx context.Context, f ReadState, asset ids.ID, -) (bool, []byte, uint8, []byte, uint64, ed25519.PublicKey, bool, error) { +) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) { values, errs := f(ctx, [][]byte{AssetKey(asset)}) return innerGetAsset(values[0], errs[0]) } @@ -308,7 +307,7 @@ func GetAsset( ctx context.Context, im state.Immutable, asset ids.ID, -) (bool, []byte, uint8, []byte, uint64, ed25519.PublicKey, bool, error) { +) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) { k := AssetKey(asset) return innerGetAsset(im.GetValue(ctx, k)) } @@ -316,12 +315,12 @@ func GetAsset( func innerGetAsset( v []byte, err error, -) (bool, []byte, uint8, []byte, uint64, ed25519.PublicKey, bool, error) { +) (bool, []byte, uint8, []byte, uint64, codec.Address, bool, error) { if errors.Is(err, database.ErrNotFound) { - return false, nil, 0, nil, 0, ed25519.EmptyPublicKey, false, nil + return false, nil, 0, nil, 0, codec.EmptyAddress, false, nil } if err != nil { - return false, nil, 0, nil, 0, ed25519.EmptyPublicKey, false, err + return false, nil, 0, nil, 0, codec.EmptyAddress, false, err } symbolLen := binary.BigEndian.Uint16(v) symbol := v[consts.Uint16Len : consts.Uint16Len+symbolLen] @@ -329,10 +328,10 @@ func innerGetAsset( metadataLen := binary.BigEndian.Uint16(v[consts.Uint16Len+symbolLen+consts.Uint8Len:]) metadata := v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len : consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen] supply := binary.BigEndian.Uint64(v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen:]) - var pk ed25519.PublicKey - copy(pk[:], v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len:]) - warp := v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len+ed25519.PublicKeyLen] == 0x1 - return true, symbol, decimals, metadata, supply, pk, warp, nil + 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 } func SetAsset( @@ -343,13 +342,13 @@ func SetAsset( decimals uint8, metadata []byte, supply uint64, - owner ed25519.PublicKey, + 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+ed25519.PublicKeyLen+1) + v := make([]byte, consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len+codec.AddressLen+1) binary.BigEndian.PutUint16(v, uint16(symbolLen)) copy(v[consts.Uint16Len:], symbol) v[consts.Uint16Len+symbolLen] = decimals @@ -361,7 +360,7 @@ func SetAsset( if warp { b = 0x1 } - v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len+ed25519.PublicKeyLen] = b + v[consts.Uint16Len+symbolLen+consts.Uint8Len+consts.Uint16Len+metadataLen+consts.Uint64Len+codec.AddressLen] = b return mu.Insert(ctx, k, v) } @@ -388,10 +387,10 @@ func SetOrder( out ids.ID, outTick uint64, supply uint64, - owner ed25519.PublicKey, + owner codec.Address, ) error { k := OrderKey(txID) - v := make([]byte, consts.IDLen*2+consts.Uint64Len*3+ed25519.PublicKeyLen) + v := make([]byte, consts.IDLen*2+consts.Uint64Len*3+codec.AddressLen) copy(v, in[:]) binary.BigEndian.PutUint64(v[consts.IDLen:], inTick) copy(v[consts.IDLen+consts.Uint64Len:], out[:]) @@ -412,7 +411,7 @@ func GetOrder( ids.ID, // out uint64, // outTick uint64, // remaining - ed25519.PublicKey, // owner + codec.Address, // owner error, ) { k := OrderKey(order) @@ -432,7 +431,7 @@ func GetOrderFromState( ids.ID, // out uint64, // outTick uint64, // remaining - ed25519.PublicKey, // owner + codec.Address, // owner error, ) { values, errs := f(ctx, [][]byte{OrderKey(order)}) @@ -446,14 +445,14 @@ func innerGetOrder(v []byte, err error) ( ids.ID, // out uint64, // outTick uint64, // remaining - ed25519.PublicKey, // owner + codec.Address, // owner error, ) { if errors.Is(err, database.ErrNotFound) { - return false, ids.Empty, 0, ids.Empty, 0, 0, ed25519.EmptyPublicKey, nil + return false, ids.Empty, 0, ids.Empty, 0, 0, codec.EmptyAddress, nil } if err != nil { - return false, ids.Empty, 0, ids.Empty, 0, 0, ed25519.EmptyPublicKey, err + return false, ids.Empty, 0, ids.Empty, 0, 0, codec.EmptyAddress, err } var in ids.ID copy(in[:], v[:consts.IDLen]) @@ -462,7 +461,7 @@ func innerGetOrder(v []byte, err error) ( copy(out[:], v[consts.IDLen+consts.Uint64Len:consts.IDLen*2+consts.Uint64Len]) outTick := binary.BigEndian.Uint64(v[consts.IDLen*2+consts.Uint64Len:]) supply := binary.BigEndian.Uint64(v[consts.IDLen*2+consts.Uint64Len*2:]) - var owner ed25519.PublicKey + var owner codec.Address copy(owner[:], v[consts.IDLen*2+consts.Uint64Len*3:]) return true, in, inTick, out, outTick, supply, owner, nil } diff --git a/examples/tokenvm/tests/e2e/e2e_test.go b/examples/tokenvm/tests/e2e/e2e_test.go index 4d9d3da210..4ec71c15a3 100644 --- a/examples/tokenvm/tests/e2e/e2e_test.go +++ b/examples/tokenvm/tests/e2e/e2e_test.go @@ -17,12 +17,12 @@ 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/codec" "github.com/ava-labs/hypersdk/crypto/ed25519" "github.com/ava-labs/hypersdk/examples/tokenvm/actions" "github.com/ava-labs/hypersdk/examples/tokenvm/auth" "github.com/ava-labs/hypersdk/examples/tokenvm/consts" trpc "github.com/ava-labs/hypersdk/examples/tokenvm/rpc" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/rpc" hutils "github.com/ava-labs/hypersdk/utils" "github.com/fatih/color" @@ -405,20 +405,22 @@ var _ = ginkgo.BeforeSuite(func() { } // Load default pk - priv, err = ed25519.HexToKey( + privBytes, err := codec.LoadHex( "323b1d8f4eed5f0da9da93071b034f2dce9d2d22692c172f3cb252a64ddfafd01b057de320297c29ad0c1f589ea216869cf1938d88c9fbd70d6748323dbf2fa7", //nolint:lll + ed25519.PrivateKeyLen, ) gomega.Ω(err).Should(gomega.BeNil()) + priv = ed25519.PrivateKey(privBytes) factory = auth.NewED25519Factory(priv) - rsender = priv.PublicKey() - sender = utils.Address(rsender) + rsender = auth.NewED25519Address(priv.PublicKey()) + sender = codec.MustAddressBech32(consts.HRP, rsender) hutils.Outf("\n{{yellow}}$ loaded address:{{/}} %s\n\n", sender) }) var ( priv ed25519.PrivateKey factory *auth.ED25519Factory - rsender ed25519.PublicKey + rsender codec.Address sender string instancesA []instance @@ -518,7 +520,7 @@ var _ = ginkgo.Describe("[Test]", func() { other, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) - aother := utils.Address(other.PublicKey()) + aother := auth.NewED25519Address(other.PublicKey()) ginkgo.By("issue Transfer to the first node", func() { // Generate transaction @@ -529,7 +531,7 @@ var _ = ginkgo.Describe("[Test]", func() { parser, nil, &actions.Transfer{ - To: other.PublicKey(), + To: aother, Value: sendAmount, }, factory, @@ -576,7 +578,7 @@ var _ = ginkgo.Describe("[Test]", func() { } // Check balance of recipient - balance, err := inst.tcli.Balance(context.Background(), aother, ids.Empty) + balance, err := inst.tcli.Balance(context.Background(), codec.MustAddressBech32(consts.HRP, aother), ids.Empty) gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(balance).Should(gomega.Equal(sendAmount)) } @@ -586,7 +588,7 @@ 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 := utils.Address(other.PublicKey()) + aother := codec.MustAddressBech32(consts.HRP, auth.NewED25519Address(other.PublicKey())) source, err := ids.FromString(blockchainIDA) gomega.Ω(err).Should(gomega.BeNil()) destination, err := ids.FromString(blockchainIDB) @@ -607,7 +609,7 @@ var _ = ginkgo.Describe("[Test]", func() { parser, nil, &actions.ExportAsset{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Asset: ids.Empty, Value: sendAmount, Return: false, @@ -653,7 +655,7 @@ var _ = ginkgo.Describe("[Test]", func() { parser, nil, &actions.Transfer{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Asset: ids.Empty, Value: 500_000_000, }, @@ -794,7 +796,7 @@ var _ = ginkgo.Describe("[Test]", func() { 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(utils.Address(ed25519.EmptyPublicKey))) + gomega.Ω(owner).Should(gomega.Equal(codec.MustAddressBech32(consts.HRP, codec.EmptyAddress))) gomega.Ω(warp).Should(gomega.BeTrue()) }) @@ -887,7 +889,7 @@ var _ = ginkgo.Describe("[Test]", func() { 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(utils.Address(ed25519.EmptyPublicKey))) + gomega.Ω(owner).Should(gomega.Equal(codec.MustAddressBech32(consts.HRP, codec.EmptyAddress))) gomega.Ω(warp).Should(gomega.BeTrue()) }) @@ -1019,7 +1021,7 @@ var _ = ginkgo.Describe("[Test]", func() { parser, nil, &actions.ExportAsset{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Asset: newAsset, Value: 2900, Return: true, @@ -1180,7 +1182,7 @@ var _ = ginkgo.Describe("[Test]", func() { parser, nil, &actions.ExportAsset{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Asset: ids.Empty, // becomes newAsset Value: 2000, Return: false, @@ -1532,7 +1534,7 @@ func generateBlocks( parser, nil, &actions.Transfer{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Value: 1, }, factory, @@ -1600,7 +1602,7 @@ func acceptTransaction(cli *rpc.JSONRPCClient, tcli *trpc.JSONRPCClient) { parser, nil, &actions.Transfer{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Value: sendAmount, }, factory, diff --git a/examples/tokenvm/tests/integration/integration_test.go b/examples/tokenvm/tests/integration/integration_test.go index 78556f99a1..f39781395b 100644 --- a/examples/tokenvm/tests/integration/integration_test.go +++ b/examples/tokenvm/tests/integration/integration_test.go @@ -48,7 +48,6 @@ import ( "github.com/ava-labs/hypersdk/examples/tokenvm/controller" "github.com/ava-labs/hypersdk/examples/tokenvm/genesis" trpc "github.com/ava-labs/hypersdk/examples/tokenvm/rpc" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" ) var ( @@ -95,12 +94,12 @@ func init() { var ( priv ed25519.PrivateKey factory *auth.ED25519Factory - rsender ed25519.PublicKey + rsender codec.Address sender string priv2 ed25519.PrivateKey factory2 *auth.ED25519Factory - rsender2 ed25519.PublicKey + rsender2 codec.Address sender2 string asset1 []byte @@ -144,8 +143,8 @@ var _ = ginkgo.BeforeSuite(func() { priv, err = ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) factory = auth.NewED25519Factory(priv) - rsender = priv.PublicKey() - sender = utils.Address(rsender) + rsender = auth.NewED25519Address(priv.PublicKey()) + sender = codec.MustAddressBech32(tconsts.HRP, rsender) log.Debug( "generated key", zap.String("addr", sender), @@ -155,8 +154,8 @@ var _ = ginkgo.BeforeSuite(func() { priv2, err = ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) factory2 = auth.NewED25519Factory(priv2) - rsender2 = priv2.PublicKey() - sender2 = utils.Address(rsender2) + rsender2 = auth.NewED25519Address(priv2.PublicKey()) + sender2 = codec.MustAddressBech32(tconsts.HRP, rsender2) log.Debug( "generated key", zap.String("addr", sender2), @@ -277,7 +276,7 @@ var _ = ginkgo.BeforeSuite(func() { gomega.Ω(decimals).Should(gomega.Equal(uint8(tconsts.Decimals))) gomega.Ω(string(metadata)).Should(gomega.Equal(tconsts.Name)) gomega.Ω(supply).Should(gomega.Equal(csupply)) - gomega.Ω(owner).Should(gomega.Equal(utils.Address(ed25519.EmptyPublicKey))) + gomega.Ω(owner).Should(gomega.Equal(codec.MustAddressBech32(tconsts.HRP, codec.EmptyAddress))) gomega.Ω(warp).Should(gomega.BeFalse()) } blocks = []snowman.Block{} @@ -437,19 +436,19 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { // read: 2 keys reads, 1 had 0 chunks // create: 1 key created // modify: 1 cold key modified - transferTxConsumed := chain.Dimensions{226, 7, 12, 25, 13} + transferTxConsumed := chain.Dimensions{227, 7, 12, 25, 13} 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(283))) + gomega.Ω(results[0].Fee).Should(gomega.Equal(uint64(284))) }) 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(9899717))) + gomega.Ω(balance).To(gomega.Equal(uint64(9899716))) balance2, err := instances[1].tcli.Balance(context.Background(), sender2, ids.Empty) gomega.Ω(err).To(gomega.BeNil()) gomega.Ω(balance2).To(gomega.Equal(uint64(100000))) @@ -631,7 +630,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { other, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) transfer := &actions.Transfer{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Value: 1, } @@ -681,7 +680,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { other, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) transfer := &actions.Transfer{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Value: 1, } parser, err := instances[0].tcli.Parser(context.Background()) @@ -735,7 +734,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.Transfer{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Value: 10, Memo: []byte("hello"), }, @@ -761,7 +760,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { }, nil, &actions.Transfer{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Value: 10, Memo: make([]byte, 1000), }, @@ -794,7 +793,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.MintAsset{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Asset: assetID, Value: 10, }, @@ -994,7 +993,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { parser, nil, &actions.MintAsset{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Asset: asset1ID, Value: 10, }, @@ -1104,7 +1103,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { }, nil, &actions.MintAsset{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Asset: asset1ID, }, ) @@ -1178,7 +1177,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { }, nil, &actions.MintAsset{ - To: other.PublicKey(), + To: auth.NewED25519Address(other.PublicKey()), Value: 10, }, ) @@ -1424,7 +1423,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(orders).Should(gomega.HaveLen(1)) order := orders[0] - owner, err := utils.ParseAddress(order.Owner) + owner, err := codec.ParseAddressBech32(tconsts.HRP, order.Owner) gomega.Ω(err).Should(gomega.BeNil()) parser, err := instances[0].tcli.Parser(context.Background()) gomega.Ω(err).Should(gomega.BeNil()) @@ -1457,7 +1456,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(orders).Should(gomega.HaveLen(1)) order := orders[0] - owner, err := utils.ParseAddress(order.Owner) + owner, err := codec.ParseAddressBech32(tconsts.HRP, order.Owner) gomega.Ω(err).Should(gomega.BeNil()) parser, err := instances[0].tcli.Parser(context.Background()) gomega.Ω(err).Should(gomega.BeNil()) @@ -1490,7 +1489,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(orders).Should(gomega.HaveLen(1)) order := orders[0] - owner, err := utils.ParseAddress(order.Owner) + owner, err := codec.ParseAddressBech32(tconsts.HRP, order.Owner) gomega.Ω(err).Should(gomega.BeNil()) parser, err := instances[0].tcli.Parser(context.Background()) gomega.Ω(err).Should(gomega.BeNil()) @@ -1648,7 +1647,7 @@ var _ = ginkgo.Describe("[Tx Processing]", func() { gomega.Ω(err).Should(gomega.BeNil()) gomega.Ω(orders).Should(gomega.HaveLen(1)) order := orders[0] - owner, err := utils.ParseAddress(order.Owner) + owner, err := codec.ParseAddressBech32(tconsts.HRP, order.Owner) gomega.Ω(err).Should(gomega.BeNil()) parser, err := instances[0].tcli.Parser(context.Background()) gomega.Ω(err).Should(gomega.BeNil()) diff --git a/examples/tokenvm/tests/load/load_test.go b/examples/tokenvm/tests/load/load_test.go index b77d0b09ec..03b03c6f8e 100644 --- a/examples/tokenvm/tests/load/load_test.go +++ b/examples/tokenvm/tests/load/load_test.go @@ -36,6 +36,7 @@ import ( "go.uber.org/zap" "github.com/ava-labs/hypersdk/chain" + "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/pebble" @@ -49,7 +50,6 @@ import ( "github.com/ava-labs/hypersdk/examples/tokenvm/controller" "github.com/ava-labs/hypersdk/examples/tokenvm/genesis" trpc "github.com/ava-labs/hypersdk/examples/tokenvm/rpc" - "github.com/ava-labs/hypersdk/examples/tokenvm/utils" "github.com/ava-labs/hypersdk/rpc" ) @@ -89,7 +89,7 @@ type instance struct { type account struct { priv ed25519.PrivateKey factory *auth.ED25519Factory - rsender ed25519.PublicKey + rsender codec.Address sender string } @@ -178,8 +178,8 @@ var _ = ginkgo.BeforeSuite(func() { var err error priv, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) - rsender := priv.PublicKey() - sender := utils.Address(rsender) + rsender := auth.NewED25519Address(priv.PublicKey()) + sender := codec.MustAddressBech32(consts.HRP, rsender) root = &account{priv, auth.NewED25519Factory(priv), rsender, sender} log.Debug( "generated root key", @@ -193,7 +193,8 @@ var _ = ginkgo.BeforeSuite(func() { gen.MinUnitPrice = chain.Dimensions{1, 1, 1, 1, 1} // target must be set less than max, otherwise we will iterate through all txs in mempool gen.WindowTargetUnits = chain.Dimensions{hconsts.NetworkSizeLimit - 10*units.KiB, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64} // disable unit price increase - gen.MaxBlockUnits = chain.Dimensions{hconsts.NetworkSizeLimit, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64} + // leave room for block header + gen.MaxBlockUnits = chain.Dimensions{hconsts.NetworkSizeLimit - units.KiB, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64, hconsts.MaxUint64} gen.MinBlockGap = 0 // don't require time between blocks gen.ValidityWindow = 1_000 * hconsts.MillisecondsPerSecond // txs shouldn't expire gen.CustomAllocation = []*genesis.CustomAllocation{ @@ -265,7 +266,7 @@ var _ = ginkgo.BeforeSuite(func() { nil, []byte( fmt.Sprintf( - `{%s"signatureVerificationCores":%d, "rootGenerationCores":%d, "transactionExecutionCores":%d, "mempoolSize":%d, "mempoolPayerSize":%d, "testMode":true}`, + `{%s"signatureVerificationCores":%d, "rootGenerationCores":%d, "transactionExecutionCores":%d, "mempoolSize":%d, "mempoolSponsorSize":%d, "testMode":true}`, tracePrefix, numWorkers/3, numWorkers/3, @@ -380,8 +381,8 @@ var _ = ginkgo.Describe("load tests vm", func() { for i := 0; i < accts; i++ { tpriv, err := ed25519.GeneratePrivateKey() gomega.Ω(err).Should(gomega.BeNil()) - trsender := tpriv.PublicKey() - tsender := utils.Address(trsender) + trsender := auth.NewED25519Address(tpriv.PublicKey()) + tsender := codec.MustAddressBech32(consts.HRP, trsender) senders[i] = &account{tpriv, auth.NewED25519Factory(tpriv), trsender, tsender} } }) @@ -529,7 +530,7 @@ var _ = ginkgo.Describe("load tests vm", func() { func issueSimpleTx( i *instance, - to ed25519.PublicKey, + to codec.Address, amount uint64, factory chain.AuthFactory, ) (ids.ID, error) { diff --git a/examples/tokenvm/utils/utils.go b/examples/tokenvm/utils/utils.go deleted file mode 100644 index 98b53ea970..0000000000 --- a/examples/tokenvm/utils/utils.go +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. -// See the file LICENSE for licensing terms. - -package utils - -import ( - "github.com/ava-labs/hypersdk/crypto/ed25519" - - "github.com/ava-labs/hypersdk/examples/tokenvm/consts" -) - -func Address(pk ed25519.PublicKey) string { - return ed25519.Address(consts.HRP, pk) -} - -func ParseAddress(s string) (ed25519.PublicKey, error) { - return ed25519.ParseAddress(consts.HRP, s) -} diff --git a/mempool/mempool.go b/mempool/mempool.go index 5503cc4115..8f253482b3 100644 --- a/mempool/mempool.go +++ b/mempool/mempool.go @@ -12,6 +12,7 @@ import ( "github.com/ava-labs/avalanchego/trace" "github.com/ava-labs/avalanchego/utils/math" "github.com/ava-labs/avalanchego/utils/set" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/eheap" "github.com/ava-labs/hypersdk/list" "go.opentelemetry.io/otel/attribute" @@ -22,7 +23,7 @@ const maxPrealloc = 4_096 type Item interface { eheap.Item - Payer() string + Sponsor() codec.Address Size() int } @@ -33,15 +34,15 @@ type Mempool[T Item] struct { pendingSize int // bytes - maxSize int - maxPayerSize int // Maximum items allowed by a single payer + maxSize int + maxSponsorSize int // Maximum items allowed by a single sponsor queue *list.List[T] eh *eheap.ExpiryHeap[*list.Element[T]] // owned tracks the number of items in the mempool owned by a single - // [Payer] - owned map[string]int + // [Sponsor] + owned map[codec.Address]int // streamedItems have been removed from the mempool during streaming // and should not be re-added by calls to [Add]. @@ -50,8 +51,8 @@ type Mempool[T Item] struct { nextStream []T nextStreamFetched bool - // payers that are exempt from [maxPayerSize] - exemptPayers set.Set[string] + // sponsors that are exempt from [maxSponsorSize] + exemptSponsors set.Set[codec.Address] } // New creates a new [Mempool]. [maxSize] must be > 0 or else the @@ -59,29 +60,29 @@ type Mempool[T Item] struct { func New[T Item]( tracer trace.Tracer, maxSize int, // items - maxPayerSize int, - exemptPayers [][]byte, + maxSponsorSize int, + exemptSponsors []codec.Address, ) *Mempool[T] { m := &Mempool[T]{ tracer: tracer, - maxSize: maxSize, - maxPayerSize: maxPayerSize, + maxSize: maxSize, + maxSponsorSize: maxSponsorSize, queue: &list.List[T]{}, eh: eheap.New[*list.Element[T]](math.Min(maxSize, maxPrealloc)), - owned: map[string]int{}, - exemptPayers: set.Set[string]{}, + owned: map[codec.Address]int{}, + exemptSponsors: set.Set[codec.Address]{}, } - for _, payer := range exemptPayers { - m.exemptPayers.Add(string(payer)) + for _, sponsor := range exemptSponsors { + m.exemptSponsors.Add(sponsor) } return m } func (m *Mempool[T]) removeFromOwned(item T) { - sender := item.Payer() + sender := item.Sponsor() items, ok := m.owned[sender] if !ok { // May no longer be populated @@ -106,7 +107,7 @@ func (m *Mempool[T]) Has(ctx context.Context, itemID ids.ID) bool { } // Add pushes all new items from [items] to m. Does not add a item if -// the item payer is not exempt and their items in the mempool exceed m.maxPayerSize. +// the item sponsor is not exempt and their items in the mempool exceed m.maxSponsorSize. // If the size of m exceeds m.maxSize, Add pops the lowest value item // from m.eh. func (m *Mempool[T]) Add(ctx context.Context, items []T) { @@ -121,7 +122,7 @@ func (m *Mempool[T]) Add(ctx context.Context, items []T) { func (m *Mempool[T]) add(items []T, front bool) { for _, item := range items { - sender := item.Payer() + sender := item.Sponsor() // Ensure no duplicate itemID := item.ID() @@ -135,7 +136,7 @@ func (m *Mempool[T]) add(items []T, front bool) { // Ensure sender isn't abusing mempool senderItems := m.owned[sender] - if !m.exemptPayers.Contains(sender) && senderItems == m.maxPayerSize { + if !m.exemptSponsors.Contains(sender) && senderItems == m.maxSponsorSize { continue // do nothing, wait for items to expire } diff --git a/mempool/mempool_test.go b/mempool/mempool_test.go index 13e3270678..d6287c004d 100644 --- a/mempool/mempool_test.go +++ b/mempool/mempool_test.go @@ -8,16 +8,17 @@ import ( "testing" "github.com/ava-labs/avalanchego/ids" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/trace" "github.com/golang/mock/gomock" "github.com/stretchr/testify/require" ) -const testPayer = "testPayer" +var testSponsor = codec.CreateAddress(1, ids.GenerateTestID()) type TestItem struct { id ids.ID - payer string + sponsor codec.Address timestamp int64 } @@ -25,8 +26,8 @@ func (mti *TestItem) ID() ids.ID { return mti.id } -func (mti *TestItem) Payer() string { - return mti.payer +func (mti *TestItem) Sponsor() codec.Address { + return mti.sponsor } func (mti *TestItem) Expiry() int64 { @@ -37,11 +38,11 @@ func (*TestItem) Size() int { return 2 // distinguish from len } -func GenerateTestItem(payer string, t int64) *TestItem { +func GenerateTestItem(sponsor codec.Address, t int64) *TestItem { id := ids.GenerateTestID() return &TestItem{ id: id, - payer: payer, + sponsor: sponsor, timestamp: t, } } @@ -56,7 +57,7 @@ func TestMempool(t *testing.T) { txm := New[*TestItem](tracer, 3, 16, nil) for _, i := range []int64{100, 200, 300, 400} { - item := GenerateTestItem(testPayer, i) + item := GenerateTestItem(testSponsor, i) items := []*TestItem{item} txm.Add(ctx, items) } @@ -75,7 +76,7 @@ func TestMempoolAddDuplicates(t *testing.T) { tracer, _ := trace.New(&trace.Config{Enabled: false}) txm := New[*TestItem](tracer, 3, 16, nil) // Generate item - item := GenerateTestItem(testPayer, 300) + item := GenerateTestItem(testSponsor, 300) items := []*TestItem{item} txm.Add(ctx, items) require.Equal(1, txm.Len(ctx), "Item not added.") @@ -87,29 +88,28 @@ func TestMempoolAddDuplicates(t *testing.T) { require.Equal(1, txm.Len(ctx), "Item not added.") } -func TestMempoolAddExceedMaxPayerSize(t *testing.T) { - // Payer1 has reached his max - // Payer2 is exempt from max size +func TestMempoolAddExceedMaxSponsorSize(t *testing.T) { + // Sponsor1 has reached his max + // Sponsor2 is exempt from max size require := require.New(t) ctrl := gomock.NewController(t) defer ctrl.Finish() ctx := context.TODO() tracer, _ := trace.New(&trace.Config{Enabled: false}) - exemptPayer := "IAMEXEMPT" - payer := "notexempt" - exemptPayers := []byte(exemptPayer) - // Non exempt payers max of 4 - txm := New[*TestItem](tracer, 20, 4, [][]byte{exemptPayers}) - // Add 6 transactions for each payer + exemptSponsor := codec.CreateAddress(99, ids.GenerateTestID()) + sponsor := codec.CreateAddress(4, ids.GenerateTestID()) + // Non exempt sponsors max of 4 + txm := New[*TestItem](tracer, 20, 4, []codec.Address{exemptSponsor}) + // Add 6 transactions for each sponsor for i := int64(0); i <= 5; i++ { - itemPayer := GenerateTestItem(payer, i) - itemExempt := GenerateTestItem(exemptPayer, i) - items := []*TestItem{itemPayer, itemExempt} + itemSponsor := GenerateTestItem(sponsor, i) + itemExempt := GenerateTestItem(exemptSponsor, i) + items := []*TestItem{itemSponsor, itemExempt} txm.Add(ctx, items) } require.Equal(10, txm.Len(ctx), "Mempool has incorrect txs.") - require.Equal(4, txm.owned[payer], "Payer has incorrect txs.") - require.Equal(6, txm.owned[exemptPayer], "Payer has incorrect txs.") + require.Equal(4, txm.owned[sponsor], "Sponsor has incorrect txs.") + require.Equal(6, txm.owned[exemptSponsor], "Sponsor has incorrect txs.") } func TestMempoolAddExceedMaxSize(t *testing.T) { @@ -122,7 +122,7 @@ func TestMempoolAddExceedMaxSize(t *testing.T) { txm := New[*TestItem](tracer, 3, 20, nil) // Add more tx's than txm.maxSize for i := int64(0); i < 10; i++ { - item := GenerateTestItem(testPayer, i) + item := GenerateTestItem(testSponsor, i) items := []*TestItem{item} txm.Add(ctx, items) if i < 3 { @@ -137,8 +137,8 @@ func TestMempoolAddExceedMaxSize(t *testing.T) { require.True(ok) require.Equal(i, popped.Expiry(), "Mempool did not pop correct tx.") } - _, ok := txm.owned[testPayer] - require.False(ok, "Payer not removed from owned.") + _, ok := txm.owned[testSponsor] + require.False(ok, "Sponsor not removed from owned.") require.Equal(0, txm.Len(ctx), "Mempool has incorrect number of txs.") } @@ -151,12 +151,12 @@ func TestMempoolRemoveTxs(t *testing.T) { txm := New[*TestItem](tracer, 3, 20, nil) // Add - item := GenerateTestItem(testPayer, 10) + item := GenerateTestItem(testSponsor, 10) items := []*TestItem{item} txm.Add(ctx, items) require.True(txm.Has(ctx, item.ID()), "TX not included") // Remove - itemNotIn := GenerateTestItem(testPayer, 10) + itemNotIn := GenerateTestItem(testSponsor, 10) items = []*TestItem{item, itemNotIn} txm.Remove(ctx, items) require.Equal(0, txm.Len(ctx), "Mempool has incorrect number of txs.") @@ -172,7 +172,7 @@ func TestMempoolSetMinTimestamp(t *testing.T) { txm := New[*TestItem](tracer, 20, 20, nil) // Add more tx's than txm.maxSize for i := int64(0); i < 10; i++ { - item := GenerateTestItem(testPayer, i) + item := GenerateTestItem(testSponsor, i) items := []*TestItem{item} txm.Add(ctx, items) require.True(txm.Has(ctx, item.ID()), "TX not included") diff --git a/rpc/websocket_client.go b/rpc/websocket_client.go index f375fd3ac3..270cca17b6 100644 --- a/rpc/websocket_client.go +++ b/rpc/websocket_client.go @@ -161,6 +161,10 @@ func (c *WebSocketClient) RegisterTx(tx *chain.Transaction) error { } // ListenForTx listens for responses from the streamingServer. +// +// TODO: add the option to subscribe to a single TxID to avoid +// trampling other listeners (could have an intermediate tracking +// layer in the client so no changes required in the server). func (c *WebSocketClient) ListenTx(ctx context.Context) (ids.ID, error, *chain.Result, error) { select { case msg := <-c.pendingTxs: diff --git a/utils/errors.go b/utils/errors.go new file mode 100644 index 0000000000..084556e334 --- /dev/null +++ b/utils/errors.go @@ -0,0 +1,8 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utils + +import "errors" + +var ErrInvalidSize = errors.New("invalid size") diff --git a/utils/utils.go b/utils/utils.go index c63dc9fc93..2635e1aa4b 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -98,3 +98,21 @@ func UnixRMilli(now, add int64) int64 { t := now + add return t - t%consts.MillisecondsPerSecond } + +// SaveBytes writes [b] to a file [filename]. If filename does +// not exist, it creates a new file with read/write permissions (0o600). +func SaveBytes(filename string, b []byte) error { + return os.WriteFile(filename, b, 0o600) +} + +// LoadBytes returns bytes stored at a file [filename]. +func LoadBytes(filename string, expectedSize int) ([]byte, error) { + bytes, err := os.ReadFile(filename) + if err != nil { + return nil, err + } + if expectedSize != -1 && len(bytes) != expectedSize { + return nil, ErrInvalidSize + } + return bytes, nil +} diff --git a/utils/utils_test.go b/utils/utils_test.go new file mode 100644 index 0000000000..be9915fb1d --- /dev/null +++ b/utils/utils_test.go @@ -0,0 +1,87 @@ +// Copyright (C) 2023, Ava Labs, Inc. All rights reserved. +// See the file LICENSE for licensing terms. + +package utils + +import ( + "bytes" + "os" + "path/filepath" + "testing" + + "github.com/ava-labs/avalanchego/ids" + "github.com/stretchr/testify/require" +) + +func TestSaveBytes(t *testing.T) { + require := require.New(t) + + tempDir := os.TempDir() + filename := filepath.Join(tempDir, "SaveBytes") + + id := ids.GenerateTestID() + require.NoError(SaveBytes(filename, id[:]), "Error during call to SaveBytes") + require.FileExists(filename, "SaveBytes did not create file") + + // Check correct key was saved in file + bytes, err := os.ReadFile(filename) + var lid ids.ID + copy(lid[:], bytes) + require.NoError(err, "Reading saved file threw an error") + require.Equal(id, lid, "ID is different than saved key") + + // Remove File + _ = os.Remove(filename) +} + +func TestLoadBytesIncorrectLength(t *testing.T) { + // Creates dummy file with invalid size + require := require.New(t) + invalidBytes := []byte{1, 2, 3, 4, 5} + + // Writes + f, err := os.CreateTemp("", "TestLoadBytes*") + require.NoError(err) + fileName := f.Name() + err = os.WriteFile(fileName, invalidBytes, 0o600) + require.NoError(err, "Error writing using OS during tests") + err = f.Close() + require.NoError(err, "Error closing file during tests") + + // Validate + _, err = LoadBytes(fileName, ids.IDLen) + require.ErrorIs(err, ErrInvalidSize) + + // Remove file + _ = os.Remove(fileName) +} + +func TestLoadKeyInvalidFile(t *testing.T) { + require := require.New(t) + + filename := "FileNameDoesntExist" + _, err := LoadBytes(filename, ids.IDLen) + require.Error(err) +} + +func TestLoadBytes(t *testing.T) { + require := require.New(t) + + // Creates dummy file with valid size + f, err := os.CreateTemp("", "TestLoadKey*") + require.NoError(err) + fileName := f.Name() + id := ids.GenerateTestID() + _, err = f.Write(id[:]) + require.NoError(err) + err = f.Close() + require.NoError(err) + + // Validate + lid, err := LoadBytes(fileName, ids.IDLen) + require.NoError(err) + require.True(bytes.Equal(lid, id[:])) + + // Remove + _ = os.Remove(fileName) +} diff --git a/vm/dependencies.go b/vm/dependencies.go index 886d887e55..2b74ed9e00 100644 --- a/vm/dependencies.go +++ b/vm/dependencies.go @@ -17,6 +17,7 @@ import ( "github.com/ava-labs/hypersdk/builder" "github.com/ava-labs/hypersdk/chain" + "github.com/ava-labs/hypersdk/codec" "github.com/ava-labs/hypersdk/gossiper" "github.com/ava-labs/hypersdk/state" trace "github.com/ava-labs/hypersdk/trace" @@ -30,8 +31,8 @@ type Config interface { GetSignatureVerificationCores() int GetRootGenerationCores() int GetTransactionExecutionCores() int - GetMempoolPayerSize() int - GetMempoolExemptPayers() [][]byte + GetMempoolSponsorSize() int + GetMempoolExemptSponsors() []codec.Address GetVerifySignatures() bool GetStreamingBacklogSize() int GetStateHistoryLength() int // how many roots back of data to keep to serve state queries diff --git a/vm/vm.go b/vm/vm.go index f761a483c2..1b09ac0eba 100644 --- a/vm/vm.go +++ b/vm/vm.go @@ -247,8 +247,8 @@ func (vm *VM) Initialize( vm.mempool = mempool.New[*chain.Transaction]( vm.tracer, vm.config.GetMempoolSize(), - vm.config.GetMempoolPayerSize(), - vm.config.GetMempoolExemptPayers(), + vm.config.GetMempoolSponsorSize(), + vm.config.GetMempoolExemptSponsors(), ) // Try to load last accepted @@ -743,7 +743,9 @@ func (vm *VM) buildBlock(ctx context.Context, blockContext *smblock.Context) (sn } blk, err := chain.BuildBlock(ctx, vm, preferredBlk, blockContext) if err != nil { - vm.snowCtx.Log.Warn("BuildBlock failed", zap.Error(err)) + // This is a DEBUG log because BuildBlock may fail before + // the min build gap (especially when there are no transactions). + vm.snowCtx.Log.Debug("BuildBlock failed", zap.Error(err)) return nil, err } vm.parsedBlocks.Put(blk.ID(), blk) diff --git a/x/programs/cmd/simulator/vm/actions/program_execute.go b/x/programs/cmd/simulator/vm/actions/program_execute.go index c804c184a3..65a18a78eb 100644 --- a/x/programs/cmd/simulator/vm/actions/program_execute.go +++ b/x/programs/cmd/simulator/vm/actions/program_execute.go @@ -137,7 +137,7 @@ func (*ProgramExecute) Size() int { } func (t *ProgramExecute) Marshal(p *codec.Packer) { - //TODO + // TODO } func (t *ProgramExecute) GetBalance() uint64 { @@ -145,7 +145,7 @@ func (t *ProgramExecute) GetBalance() uint64 { } func UnmarshalProgramExecute(p *codec.Packer, _ *warp.Message) (chain.Action, error) { - //TODO + // TODO return nil, nil } diff --git a/x/programs/cmd/simulator/vm/genesis/genesis.go b/x/programs/cmd/simulator/vm/genesis/genesis.go index 2926974d60..ebed35b544 100644 --- a/x/programs/cmd/simulator/vm/genesis/genesis.go +++ b/x/programs/cmd/simulator/vm/genesis/genesis.go @@ -62,8 +62,8 @@ type Genesis struct { WarmStorageValueModificationUnits uint64 `json:"warmStorageValueModificationUnits"` // per chunk // program Runtime Parameters - EnableDebugMode bool `json:"enableDebugMode"` - EnableBulkMemory bool `json:"enableBulkMemory"` + EnableDebugMode bool `json:"enableDebugMode"` + EnableBulkMemory bool `json:"enableBulkMemory"` // Allocations CustomAllocation []*CustomAllocation `json:"customAllocation"`