diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index fb2ce4079f..ce4f172587 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,6 +12,17 @@ jobs: - name: Check out code into the Go module directory uses: actions/checkout@v4 + - name: Pull IOTA SDK bindings + uses: dsaltares/fetch-gh-release-asset@master + with: + repo: 'iotaledger/iota-sdk-native-bindings' + # For now use 'latest' + #version: 'tags/v0.1.0' + regex: true + file: ".*" + target: 'sdk/' + token: ${{ secrets.GITHUB_TOKEN }} + - name: Tar temporary artifacts run: tar --exclude='temp.tar' -cf temp.tar ./ diff --git a/.gitignore b/.gitignore index 18a9fd1270..8054bf6a3a 100644 --- a/.gitignore +++ b/.gitignore @@ -43,3 +43,4 @@ go.work go.work.sum TMP* tmp.* +sdk/ \ No newline at end of file diff --git a/clients/chainclient/chainclient.go b/clients/chainclient/chainclient.go index 20148aed66..7a0ad49444 100644 --- a/clients/chainclient/chainclient.go +++ b/clients/chainclient/chainclient.go @@ -21,7 +21,7 @@ type Client struct { Layer1Client l1connection.Client WaspClient *apiclient.APIClient ChainID isc.ChainID - KeyPair *cryptolib.KeyPair + KeyPair cryptolib.VariantKeyPair } // New creates a new chainclient.Client @@ -29,7 +29,7 @@ func New( layer1Client l1connection.Client, waspClient *apiclient.APIClient, chainID isc.ChainID, - keyPair *cryptolib.KeyPair, + keyPair cryptolib.VariantKeyPair, ) *Client { return &Client{ Layer1Client: layer1Client, diff --git a/go.mod b/go.mod index 127cb182f5..c50a2ca5e1 100644 --- a/go.mod +++ b/go.mod @@ -87,6 +87,7 @@ require ( github.com/deckarep/golang-set/v2 v2.3.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/ebitengine/purego v0.6.0-alpha.5 // indirect github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect github.com/elastic/gosigar v0.14.2 // indirect github.com/ethereum/c-kzg-4844 v0.3.1 // indirect @@ -208,7 +209,7 @@ require ( go.uber.org/multierr v1.11.0 // indirect golang.org/x/mod v0.14.0 // indirect golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/tools v0.17.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect diff --git a/go.sum b/go.sum index 694569c23c..9ec0c6553e 100644 --- a/go.sum +++ b/go.sum @@ -133,6 +133,8 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/ebitengine/purego v0.6.0-alpha.5 h1:EYID3JOAdmQ4SNZYJHu9V6IqOeRQDBYxqKAg9PyoHFY= +github.com/ebitengine/purego v0.6.0-alpha.5/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik= github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= @@ -937,6 +939,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= diff --git a/packages/apilib/deploychain.go b/packages/apilib/deploychain.go index 9d9dbf5bb5..554dccd84e 100644 --- a/packages/apilib/deploychain.go +++ b/packages/apilib/deploychain.go @@ -26,7 +26,7 @@ type CreateChainParams struct { CommitteeAPIHosts []string N uint16 T uint16 - OriginatorKeyPair *cryptolib.KeyPair + OriginatorKeyPair cryptolib.VariantKeyPair Textout io.Writer Prefix string InitParams dict.Dict @@ -40,7 +40,7 @@ func DeployChain(par CreateChainParams, stateControllerAddr, govControllerAddr i if par.Textout != nil { textout = par.Textout } - originatorAddr := par.OriginatorKeyPair.GetPublicKey().AsEd25519Address() + originatorAddr := par.OriginatorKeyPair.Address() fmt.Fprint(textout, par.Prefix) fmt.Fprintf(textout, "Creating new chain\n* Owner address: %s\n* State controller: %s\n* committee size = %d\n* quorum = %d\n", @@ -79,12 +79,12 @@ func utxoIDsFromUtxoMap(utxoMap iotago.OutputSet) iotago.OutputIDs { // CreateChainOrigin creates and confirms origin transaction of the chain and init request transaction to initialize state of it func CreateChainOrigin( layer1Client l1connection.Client, - originator *cryptolib.KeyPair, + originator cryptolib.VariantKeyPair, stateController iotago.Address, governanceController iotago.Address, initParams dict.Dict, ) (isc.ChainID, error) { - originatorAddr := originator.GetPublicKey().AsEd25519Address() + originatorAddr := originator.Address() // ----------- request owner address' outputs from the ledger utxoMap, err := layer1Client.OutputMap(originatorAddr) if err != nil { diff --git a/packages/cryptolib/keypair.go b/packages/cryptolib/keypair.go index a0fccd6a47..34e1af13b4 100644 --- a/packages/cryptolib/keypair.go +++ b/packages/cryptolib/keypair.go @@ -52,6 +52,21 @@ func (k *KeyPair) GetPublicKey() *PublicKey { return k.publicKey } +func (k *KeyPair) SignBytes(data []byte) []byte { + return k.GetPrivateKey().Sign(data) +} + +func (k *KeyPair) Sign(addr iotago.Address, payload []byte) (iotago.Signature, error) { + signature := iotago.Ed25519Signature{} + copy(signature.Signature[:], k.privateKey.Sign(payload)) + copy(signature.PublicKey[:], k.publicKey.AsBytes()) + return &signature, nil +} + +func (k *KeyPair) AddressKeysForEd25519Address(addr *iotago.Ed25519Address) iotago.AddressKeys { + return k.GetPrivateKey().AddressKeysForEd25519Address(addr) +} + func (k *KeyPair) Address() *iotago.Ed25519Address { return k.GetPublicKey().AsEd25519Address() } diff --git a/packages/cryptolib/variant_keypair.go b/packages/cryptolib/variant_keypair.go new file mode 100644 index 0000000000..4205ee8dfb --- /dev/null +++ b/packages/cryptolib/variant_keypair.go @@ -0,0 +1,15 @@ +package cryptolib + +import ( + iotago "github.com/iotaledger/iota.go/v3" +) + +// VariantKeyPair originates from cryptolib.KeyPair +type VariantKeyPair interface { + GetPublicKey() *PublicKey + Address() *iotago.Ed25519Address + AsAddressSigner() iotago.AddressSigner + AddressKeysForEd25519Address(addr *iotago.Ed25519Address) iotago.AddressKeys + SignBytes(data []byte) []byte + Sign(addr iotago.Address, msg []byte) (signature iotago.Signature, err error) +} diff --git a/packages/isc/request.go b/packages/isc/request.go index 29f1d43252..61f1125906 100644 --- a/packages/isc/request.go +++ b/packages/isc/request.go @@ -78,7 +78,7 @@ type UnsignedOffLedgerRequest interface { WithGasBudget(gasBudget uint64) UnsignedOffLedgerRequest WithAllowance(allowance *Assets) UnsignedOffLedgerRequest WithSender(sender *cryptolib.PublicKey) UnsignedOffLedgerRequest - Sign(key *cryptolib.KeyPair) OffLedgerRequest + Sign(key cryptolib.VariantKeyPair) OffLedgerRequest } type OffLedgerRequest interface { diff --git a/packages/isc/request_offledger.go b/packages/isc/request_offledger.go index 149b429ac2..b93e06065d 100644 --- a/packages/isc/request_offledger.go +++ b/packages/isc/request_offledger.go @@ -186,10 +186,10 @@ func (req *OffLedgerRequestData) SenderAccount() AgentID { } // Sign signs the essence -func (req *OffLedgerRequestData) Sign(key *cryptolib.KeyPair) OffLedgerRequest { +func (req *OffLedgerRequestData) Sign(key cryptolib.VariantKeyPair) OffLedgerRequest { req.signature = offLedgerSignature{ publicKey: key.GetPublicKey(), - signature: key.GetPrivateKey().Sign(req.messageToSign()), + signature: key.SignBytes(req.messageToSign()), } return req } diff --git a/packages/origin/origin.go b/packages/origin/origin.go index afcc45e1b1..2dc5420da9 100644 --- a/packages/origin/origin.go +++ b/packages/origin/origin.go @@ -126,7 +126,7 @@ func calcStateMetadata(initParams dict.Dict, commonAccountAmount uint64, schemaV // NewChainOriginTransaction creates new origin transaction for the self-governed chain // returns the transaction and newly minted chain ID func NewChainOriginTransaction( - keyPair *cryptolib.KeyPair, + keyPair cryptolib.VariantKeyPair, stateControllerAddress iotago.Address, governanceControllerAddress iotago.Address, deposit uint64, @@ -139,7 +139,7 @@ func NewChainOriginTransaction( panic("mismatched lengths of outputs and inputs slices") } - walletAddr := keyPair.GetPublicKey().AsEd25519Address() + walletAddr := keyPair.Address() if initParams == nil { initParams = dict.New() @@ -189,13 +189,12 @@ func NewChainOriginTransaction( Inputs: txInputs.UTXOInputs(), Outputs: outputs, } - sigs, err := essence.Sign( - txInputs.OrderedSet(unspentOutputs).MustCommitment(), - keyPair.GetPrivateKey().AddressKeysForEd25519Address(walletAddr), - ) + + sigs, err := transaction.SignEssence(essence, txInputs.OrderedSet(unspentOutputs).MustCommitment(), keyPair) if err != nil { return nil, aliasOutput, isc.ChainID{}, err } + tx := &iotago.Transaction{ Essence: essence, Unlocks: transaction.MakeSignatureAndReferenceUnlocks(len(txInputs), sigs[0]), diff --git a/packages/transaction/change_gov_controller.go b/packages/transaction/change_gov_controller.go index 23767ad0b8..8dce36bbca 100644 --- a/packages/transaction/change_gov_controller.go +++ b/packages/transaction/change_gov_controller.go @@ -13,7 +13,7 @@ func NewChangeGovControllerTx( chainID iotago.AliasID, newGovController iotago.Address, utxos iotago.OutputSet, - wallet *cryptolib.KeyPair, + wallet cryptolib.VariantKeyPair, ) (*iotago.Transaction, error) { // find the correct chain UTXO var chainOutput *iotago.AliasOutput diff --git a/packages/transaction/nfttransaction.go b/packages/transaction/nfttransaction.go index b0f5204f45..1fe7aeb259 100644 --- a/packages/transaction/nfttransaction.go +++ b/packages/transaction/nfttransaction.go @@ -8,7 +8,7 @@ import ( ) type MintNFTsTransactionParams struct { - IssuerKeyPair *cryptolib.KeyPair + IssuerKeyPair cryptolib.VariantKeyPair CollectionOutputID *iotago.OutputID Target iotago.Address ImmutableMetadata [][]byte diff --git a/packages/transaction/requesttx.go b/packages/transaction/requesttx.go index 541d955827..12368dc86d 100644 --- a/packages/transaction/requesttx.go +++ b/packages/transaction/requesttx.go @@ -12,7 +12,7 @@ import ( ) type NewRequestTransactionParams struct { - SenderKeyPair *cryptolib.KeyPair + SenderKeyPair cryptolib.VariantKeyPair SenderAddress iotago.Address // might be different from the senderKP address (when sending as NFT or alias) UnspentOutputs iotago.OutputSet UnspentOutputIDs iotago.OutputIDs @@ -26,7 +26,7 @@ type NewTransferTransactionParams struct { FungibleTokens *isc.Assets SendOptions isc.SendOptions SenderAddress iotago.Address - SenderKeyPair *cryptolib.KeyPair + SenderKeyPair cryptolib.VariantKeyPair TargetAddress iotago.Address UnspentOutputs iotago.OutputSet UnspentOutputIDs iotago.OutputIDs diff --git a/packages/transaction/rotate.go b/packages/transaction/rotate.go index 61a41a8c61..0939b87ff5 100644 --- a/packages/transaction/rotate.go +++ b/packages/transaction/rotate.go @@ -15,7 +15,7 @@ func NewRotateChainStateControllerTx( newStateController iotago.Address, chainOutputID iotago.OutputID, chainOutput iotago.Output, - kp *cryptolib.KeyPair, + kp cryptolib.VariantKeyPair, ) (*iotago.Transaction, error) { o, ok := chainOutput.(*iotago.AliasOutput) if !ok { diff --git a/packages/transaction/sign_essence.go b/packages/transaction/sign_essence.go new file mode 100644 index 0000000000..35c7a57871 --- /dev/null +++ b/packages/transaction/sign_essence.go @@ -0,0 +1,47 @@ +package transaction + +import ( + iotago "github.com/iotaledger/iota.go/v3" + "github.com/iotaledger/wasp/packages/cryptolib" +) + +// alternateSignEssence is basically a 1:1 copy of iota.go with the difference that you can inject your own AddressSigner. +// This will ignore passed addressKeys and only use the passed AddressSigner. +// This is important for HW-wallets where the private key is unknown. +func alternateSignEssence(essence *iotago.TransactionEssence, inputsCommitment []byte, signer iotago.AddressSigner, addrKeys ...iotago.AddressKeys) ([]iotago.Signature, error) { + // SignBytes produces signatures signing the essence for every given AddressKeys. + // The produced signatures are in the same order as the AddressKeys. + if inputsCommitment == nil || len(inputsCommitment) != iotago.InputsCommitmentLength { + return nil, iotago.ErrInvalidInputsCommitment + } + + copy(essence.InputsCommitment[:], inputsCommitment) + + signMsg, err := essence.SigningMessage() + if err != nil { + return nil, err + } + + sigs := make([]iotago.Signature, len(addrKeys)) + + if signer == nil { + signer = iotago.NewInMemoryAddressSigner(addrKeys...) + } + + for i, v := range addrKeys { + sig, err := signer.Sign(v.Address, signMsg) + if err != nil { + return nil, err + } + sigs[i] = sig + } + + return sigs, nil +} + +func SignEssence(essence *iotago.TransactionEssence, inputsCommitment []byte, keyPair cryptolib.VariantKeyPair) ([]iotago.Signature, error) { + signer := keyPair.AsAddressSigner() + addressKeys := keyPair.AddressKeysForEd25519Address(keyPair.Address()) + + return alternateSignEssence(essence, inputsCommitment, signer, addressKeys) +} diff --git a/packages/transaction/util.go b/packages/transaction/util.go index 8ce3d7c4f8..41823eabc3 100644 --- a/packages/transaction/util.go +++ b/packages/transaction/util.go @@ -233,7 +233,7 @@ func MakeAnchorTransaction(essence *iotago.TransactionEssence, sig iotago.Signat } } -func CreateAndSignTx(inputs iotago.Inputs, inputsCommitment []byte, outputs iotago.Outputs, wallet *cryptolib.KeyPair, networkID uint64) (*iotago.Transaction, error) { +func CreateAndSignTx(inputs iotago.Inputs, inputsCommitment []byte, outputs iotago.Outputs, wallet cryptolib.VariantKeyPair, networkID uint64) (*iotago.Transaction, error) { unorderedEssence := &iotago.TransactionEssence{ NetworkID: networkID, Inputs: inputs, @@ -254,10 +254,7 @@ func CreateAndSignTx(inputs iotago.Inputs, inputsCommitment []byte, outputs iota } // -- - sigs, err := essence.Sign( - inputsCommitment, - wallet.GetPrivateKey().AddressKeysForEd25519Address(wallet.Address()), - ) + sigs, err := SignEssence(essence, inputsCommitment, wallet) if err != nil { return nil, err } diff --git a/tools/cluster/tests/reboot_test.go b/tools/cluster/tests/reboot_test.go index 09665f8b5b..43102d2880 100644 --- a/tools/cluster/tests/reboot_test.go +++ b/tools/cluster/tests/reboot_test.go @@ -14,6 +14,7 @@ import ( "github.com/iotaledger/wasp/clients/chainclient" "github.com/iotaledger/wasp/clients/scclient" "github.com/iotaledger/wasp/contracts/native/inccounter" + "github.com/iotaledger/wasp/packages/cryptolib" "github.com/iotaledger/wasp/packages/isc" "github.com/iotaledger/wasp/packages/kv" "github.com/iotaledger/wasp/packages/kv/codec" @@ -237,7 +238,7 @@ func TestRebootN3Single(t *testing.T) { client := env.createNewClient() tm.Step("createNewClient") - env.DepositFunds(1_000_000, client.ChainClient.KeyPair) // For Off-ledger requests to pass. + env.DepositFunds(1_000_000, client.ChainClient.KeyPair.(*cryptolib.KeyPair)) // For Off-ledger requests to pass. tm.Step("DepositFunds") icc := newIncCounterClient(t, env, client) @@ -264,7 +265,7 @@ func TestRebootN3TwoNodes(t *testing.T) { client := env.createNewClient() tm.Step("createNewClient") - env.DepositFunds(1_000_000, client.ChainClient.KeyPair) // For Off-ledger requests to pass. + env.DepositFunds(1_000_000, client.ChainClient.KeyPair.(*cryptolib.KeyPair)) // For Off-ledger requests to pass. tm.Step("DepositFunds") icc := newIncCounterClient(t, env, client) diff --git a/tools/wasp-cli/.goreleaser.yml b/tools/wasp-cli/.goreleaser.yml index 63653ab901..0b4cc37708 100644 --- a/tools/wasp-cli/.goreleaser.yml +++ b/tools/wasp-cli/.goreleaser.yml @@ -71,18 +71,35 @@ builds: - amd64 # Archives + archives: - - format: tar.gz + - # Windows + id: wasp-cli-windows + builds: + - wasp-cli-windows-amd64 + format: zip + wrap_in_directory: true + name_template: >- + {{ .ProjectName }}_{{ .Version }}_Windows_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "arm64" }}ARM64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + files: + - README.md + - LICENSE + - src: sdk/iota_sdk.dll + dst: iota_sdk.dll + + - # Linux + id: wasp-cli-linux + builds: + - wasp-cli-linux-amd64 + - wasp-cli-linux-arm64 + format: tar.gz wrap_in_directory: true - format_overrides: - - goos: windows - format: zip name_template: >- - {{ .ProjectName }}_{{ .Version }}_ - {{- if eq .Os "darwin" }}macOS_ - {{- else if eq .Os "linux" }}Linux_ - {{- else if eq .Os "windows" }}Windows_ - {{- else }}{{ .Os }}_{{ end }} + {{ .ProjectName }}_{{ .Version }}_Linux_ {{- if eq .Arch "amd64" }}x86_64 {{- else if eq .Arch "arm64" }}ARM64 {{- else if eq .Arch "386" }}i386 @@ -90,6 +107,28 @@ archives: files: - README.md - LICENSE + - src: sdk/libiota_sdk.so + dst: libiota_sdk.so + + - # MacOS + id: wasp-cli-macos + builds: + - wasp-cli-darwin-all + format: tar.gz + wrap_in_directory: true + name_template: >- + {{ .ProjectName }}_{{ .Version }}_MacOS_ + {{- if eq .Arch "amd64" }}x86_64 + {{- else if eq .Arch "arm64" }}ARM64 + {{- else if eq .Arch "386" }}i386 + {{- else }}{{ .Arch }}{{ end }} + files: + - README.md + - LICENSE + - src: sdk/libiota_sdk.dylib + dst: libiota_sdk.dylib + + # Checksum checksum: diff --git a/tools/wasp-cli/chain/deploy.go b/tools/wasp-cli/chain/deploy.go index e7ff67b727..a2b9aec5c5 100644 --- a/tools/wasp-cli/chain/deploy.go +++ b/tools/wasp-cli/chain/deploy.go @@ -84,7 +84,7 @@ func initDeployCmd() *cobra.Command { CommitteeAPIHosts: config.NodeAPIURLs([]string{node}), N: uint16(len(node)), T: uint16(quorum), - OriginatorKeyPair: wallet.Load().KeyPair, + OriginatorKeyPair: wallet.Load(), Textout: os.Stdout, GovernanceController: govController, InitParams: dict.Dict{ diff --git a/tools/wasp-cli/chain/rotate.go b/tools/wasp-cli/chain/rotate.go index 5ce02d7632..281d6fcac0 100644 --- a/tools/wasp-cli/chain/rotate.go +++ b/tools/wasp-cli/chain/rotate.go @@ -96,7 +96,7 @@ func rotateTo(chain string, newStateControllerAddr iotago.Address) { newStateControllerAddr, chainOutputID, chainOutput, - myWallet.KeyPair, + myWallet, ) log.Check(err) @@ -115,7 +115,7 @@ func rotateTo(chain string, newStateControllerAddr iotago.Address) { json, err2 := tx.MarshalJSON() log.Check(err2) - log.Printf("issuing rotation tx, signed for address: %s", myWallet.KeyPair.Address().Bech32(parameters.L1().Protocol.Bech32HRP)) + log.Printf("issuing rotation tx, signed for address: %s", myWallet.Address().Bech32(parameters.L1().Protocol.Bech32HRP)) log.Printf("rotation tx: %s", string(json)) } @@ -169,7 +169,7 @@ func initChangeGovControllerCmd() *cobra.Command { chain.AsAliasID(), newGovController, outputSet, - myWallet.KeyPair, + myWallet, ) log.Check(err) diff --git a/tools/wasp-cli/cli/cliclients/clients.go b/tools/wasp-cli/cli/cliclients/clients.go index 0d1845c664..65780c0326 100644 --- a/tools/wasp-cli/cli/cliclients/clients.go +++ b/tools/wasp-cli/cli/cliclients/clients.go @@ -68,7 +68,7 @@ func ChainClient(waspClient *apiclient.APIClient, chainID isc.ChainID) *chaincli L1Client(), waspClient, chainID, - wallet.Load().KeyPair, + wallet.Load(), ) } diff --git a/tools/wasp-cli/cli/config/config.go b/tools/wasp-cli/cli/config/config.go index 8ef18a9560..a6a04139a5 100644 --- a/tools/wasp-cli/cli/config/config.go +++ b/tools/wasp-cli/cli/config/config.go @@ -2,18 +2,24 @@ package config import ( "fmt" + "os" + "path" "time" "github.com/spf13/viper" iotago "github.com/iotaledger/iota.go/v3" + "github.com/iotaledger/wasp-wallet-sdk/types" "github.com/iotaledger/wasp/packages/isc" "github.com/iotaledger/wasp/packages/parameters" "github.com/iotaledger/wasp/packages/testutil/privtangle/privtangledefaults" + "github.com/iotaledger/wasp/tools/wasp-cli/cli" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/keychain" "github.com/iotaledger/wasp/tools/wasp-cli/log" ) var ( + BaseDir string ConfigPath string WaitForCompletion bool ) @@ -48,7 +54,49 @@ func LoadL1ParamsFromConfig() { parameters.InitL1(params) } +func locateBaseDir() string { + homeDir, err := os.UserHomeDir() + log.Check(err) + + _, err = os.Stat(homeDir) + log.Check(err) + + baseDir := path.Join(homeDir, ".wasp-cli") + _, err = os.Stat(baseDir) + + if err != nil { + err = os.Mkdir(baseDir, os.ModePerm) + log.Check(err) + } + + BaseDir = baseDir + return baseDir +} + +func locateConfigFile() string { + /* + Searches for a wasp-cli.json at the current working directory, + If not found, use the config file from the base dir (usually ~/.wasp-cli/wasp-cli.json) + */ + if ConfigPath == "" { + cwd, err := os.Getwd() + log.Check(err) + + _, err = os.Stat(path.Join(cwd, "wasp-cli.json")) + if err == nil { + ConfigPath = path.Join(cwd, "wasp-cli.json") + } else { + ConfigPath = path.Join(BaseDir, "wasp-cli.json") + } + } + + return ConfigPath +} + func Read() { + locateBaseDir() + locateConfigFile() + viper.SetConfigFile(ConfigPath) _ = viper.ReadInConfig() } @@ -77,12 +125,29 @@ func L1FaucetAddress() string { ) } +var keyChain keychain.KeyChain + +func GetKeyChain() keychain.KeyChain { + if keyChain == nil { + if keychain.IsKeyChainAvailable() { + keyChain = keychain.NewKeyChainZalando() + } else { + keyChain = keychain.NewKeyChainFile(BaseDir, cli.ReadPasswordFromStdin) + } + } + + return keyChain +} + func GetToken(node string) string { - return viper.GetString(fmt.Sprintf("authentication.wasp.%s.token", node)) + token, err := GetKeyChain().GetJWTAuthToken(node) + log.Check(err) + return token } func SetToken(node, token string) { - Set(fmt.Sprintf("authentication.wasp.%s.token", node), token) + err := GetKeyChain().SetJWTAuthToken(node, token) + log.Check(err) } func MustWaspAPIURL(nodeName string) string { @@ -135,3 +200,33 @@ func GetChain(name string) isc.ChainID { log.Check(err) return chainID } + +func GetUseLegacyDerivation() bool { + return viper.GetBool("wallet.useLegacyDerivation") +} + +func GetWalletProviderString() string { + return viper.GetString("wallet.provider") +} + +func SetWalletProviderString(provider string) { + Set("wallet.provider", provider) +} + +// GetSeedForMigration is used to migrate the seed of the config file to a certain wallet provider. +func GetSeedForMigration() string { + return viper.GetString("wallet.seed") +} + +func GetWalletLogLevel() types.ILoggerConfigLevelFilter { + logLevel := viper.GetString("wallet.loglevel") + if logLevel == "" { + return types.LevelFilterOff + } + + return types.ILoggerConfigLevelFilter(logLevel) +} + +func SetWalletLogLevel(filter types.ILoggerConfigLevelFilter) { + viper.Set("wallet.loglevel", string(filter)) +} diff --git a/tools/wasp-cli/cli/keychain/keychain.go b/tools/wasp-cli/cli/keychain/keychain.go new file mode 100644 index 0000000000..a1c877286a --- /dev/null +++ b/tools/wasp-cli/cli/keychain/keychain.go @@ -0,0 +1,47 @@ +package keychain + +import ( + "errors" + "fmt" + "time" + + "github.com/awnumar/memguard" + + "github.com/iotaledger/wasp/packages/cryptolib" +) + +const ( + strongholdKey = "wasp-cli.stronghold.key" + jwtTokenKeyPrefix = "wasp-cli.auth.jwt" + seedKey = "wasp-cli.seed" +) + +const WaspCliServiceName = "IOTAFoundation.WaspCLI" + +var ( + ErrKeyNotFound = errors.New("key not found") + + ErrTokenDoesNotExist = errors.New("jwt token not found, call 'login'") + ErrPasswordDoesNotExist = errors.New("stronghold entry not found, call 'init'") + ErrSeedDoesNotExist = errors.New("seed not found, call 'init'") + ErrSeedDoesNotMatchLength = errors.New("returned seed does not have a valid length") +) + +type KeyChain interface { + SetSeed(seed cryptolib.Seed) error + GetSeed() (*cryptolib.Seed, error) + + SetStrongholdPassword(password *memguard.Enclave) error + GetStrongholdPassword() (*memguard.Enclave, error) + + SetJWTAuthToken(node string, token string) error + GetJWTAuthToken(node string) (string, error) +} + +func jwtTokenKey(node string) string { + return fmt.Sprintf("%s.%s", jwtTokenKeyPrefix, node) +} + +func printWithTime(str string) { + fmt.Printf("[%v] %v\n", time.Now(), str) +} diff --git a/tools/wasp-cli/cli/keychain/keychain99.go b/tools/wasp-cli/cli/keychain/keychain99.go new file mode 100644 index 0000000000..cacbe75cfd --- /dev/null +++ b/tools/wasp-cli/cli/keychain/keychain99.go @@ -0,0 +1,138 @@ +package keychain + +import ( + "errors" + "runtime" + "syscall" + + "github.com/99designs/keyring" + "github.com/awnumar/memguard" + "golang.org/x/term" + + "github.com/iotaledger/wasp/packages/cryptolib" +) + +/** +Will most likely be removed and replaced by zalando/go-keyring + `keychain_file.go` +*/ + +type KeyChain99 struct { + Keyring keyring.Keyring +} + +func passwordCallback(m string) (string, error) { + printWithTime("Enter password to unlock the keychain") + passwordBytes, err := term.ReadPassword(int(syscall.Stdin)) //nolint:unconvert // int cast is needed for windows + printWithTime("") + + return string(passwordBytes), err +} + +func NewKeyRing99(baseDir string) *KeyChain99 { + printWithTime("newKeyRing") + + printWithTime("availableBackends:") + for _, b := range keyring.AvailableBackends() { + printWithTime(string(b)) + } + + ring, _ := keyring.Open(keyring.Config{ + ServiceName: WaspCliServiceName, + FileDir: baseDir, + FilePasswordFunc: passwordCallback, + KeychainPasswordFunc: passwordCallback, + }) + printWithTime("newKeyRing opened") + + return &KeyChain99{ + Keyring: ring, + } +} + +func (k *KeyChain99) Close() { + runtime.KeepAlive(k.Keyring) + k.Keyring = nil +} + +func (k *KeyChain99) SetSeed(seed cryptolib.Seed) error { + printWithTime("SetSeed start") + + err := k.Keyring.Set(keyring.Item{ + Key: seedKey, + Data: seed[:], + }) + + printWithTime("SetSeed finished") + + return err +} + +func (k *KeyChain99) GetSeed() (*cryptolib.Seed, error) { + printWithTime("GetSeed start") + + seedItem, err := k.Keyring.Get(seedKey) + if errors.Is(err, keyring.ErrKeyNotFound) { + return nil, ErrSeedDoesNotExist + } + if err != nil { + return nil, err + } + + if len(seedItem.Data) != cryptolib.SeedSize { + return nil, ErrSeedDoesNotMatchLength + } + + var seed cryptolib.Seed + copy(seed[:], seedItem.Data) + printWithTime("GetSeed finished") + + return &seed, nil +} + +func (k *KeyChain99) SetStrongholdPassword(password *memguard.Enclave) error { + buffer, err := password.Open() + if err != nil { + return err + } + defer buffer.Destroy() + + return k.Keyring.Set(keyring.Item{ + Key: strongholdKey, + Data: buffer.Data(), + }) +} + +func (k *KeyChain99) GetStrongholdPassword() (*memguard.Enclave, error) { + seedItem, err := k.Keyring.Get(strongholdKey) + if errors.Is(err, keyring.ErrKeyNotFound) { + return nil, ErrPasswordDoesNotExist + } + + return memguard.NewEnclave(seedItem.Data), nil +} + +func (k *KeyChain99) SetJWTAuthToken(node string, token string) error { + printWithTime("SetJWTAuthToken start") + + err := k.Keyring.Set(keyring.Item{ + Key: jwtTokenKey(node), + Data: []byte(token), + }) + + printWithTime("SetJWTAuthToken finished") + + return err +} + +func (k *KeyChain99) GetJWTAuthToken(node string) (string, error) { + printWithTime("GetJWTAuthToken start") + + seedItem, err := k.Keyring.Get(jwtTokenKey(node)) + // Special case. If the key is not found, return an empty token. + if errors.Is(err, keyring.ErrKeyNotFound) { + return "", nil + } + printWithTime("GetJWTAuthToken finished") + + return string(seedItem.Data), nil +} diff --git a/tools/wasp-cli/cli/keychain/keychain_file.go b/tools/wasp-cli/cli/keychain/keychain_file.go new file mode 100644 index 0000000000..c832e47a1f --- /dev/null +++ b/tools/wasp-cli/cli/keychain/keychain_file.go @@ -0,0 +1,192 @@ +package keychain + +import ( + "encoding/json" + "errors" + "os" + "path" + "strings" + + "github.com/awnumar/memguard" + jose "github.com/dvsekhvalnov/jose2go" + + "github.com/iotaledger/wasp/packages/cryptolib" +) + +const fileName = "secrets.db" + +var ErrInvalidPassword = errors.New("invalid password") + +type KeyChainFile struct { + path string + passwordCallback func() *memguard.Enclave + password *memguard.Enclave +} + +func NewKeyChainFile(path string, passwordCallback func() *memguard.Enclave) *KeyChainFile { + return &KeyChainFile{ + path: path, + passwordCallback: passwordCallback, + } +} + +func (k *KeyChainFile) promptPassword() *memguard.Enclave { + if k.password != nil { + return k.password + } + + enclave := k.passwordCallback() + k.password = enclave + + return enclave +} + +func (k *KeyChainFile) FilePath() string { + return path.Join(k.path, fileName) +} + +func (k *KeyChainFile) ReadContents() (map[string][]byte, error) { + _, err := os.Stat(k.FilePath()) + + if errors.Is(err, os.ErrNotExist) { + result, neErr := json.Marshal(struct{}{}) + if neErr != nil { + return nil, neErr + } + + neErr = os.WriteFile(k.FilePath(), result, os.ModePerm) + if neErr != nil { + return nil, neErr + } + + return map[string][]byte{}, nil + } + + content, err := os.ReadFile(k.FilePath()) + if err != nil { + return nil, err + } + + var secretsMap map[string][]byte + err = json.Unmarshal(content, &secretsMap) + if err != nil { + return nil, err + } + + return secretsMap, nil +} + +func (k *KeyChainFile) Get(key string) ([]byte, error) { + secrets, err := k.ReadContents() + if err != nil { + return nil, err + } + + if val, ok := secrets[key]; ok { + enclave := k.promptPassword() + password, err := enclave.Open() + if err != nil { + return nil, err + } + defer password.Destroy() + + payload, _, err := jose.Decode(string(val), password.String()) + if err != nil { + // There is no proper error definition available to check against, so it needs to be hardcoded here. + if strings.Contains(err.Error(), "aes.KeyUnwrap") { + return nil, ErrInvalidPassword + } + return nil, err + } + + return []byte(payload), nil + } + + return nil, ErrKeyNotFound +} + +func (k *KeyChainFile) Set(key string, value []byte) error { + secrets, err := k.ReadContents() + if err != nil { + return err + } + + enclave := k.promptPassword() + password, err := enclave.Open() + if err != nil { + return err + } + defer password.Destroy() + + token, err := jose.EncryptBytes(value, jose.PBES2_HS256_A128KW, jose.A256GCM, password.String()) + if err != nil { + return err + } + + secrets[key] = []byte(token) + payload, err := json.MarshalIndent(secrets, "", " ") + if err != nil { + return err + } + + err = os.WriteFile(k.FilePath(), payload, os.ModePerm) + if err != nil { + return err + } + + return nil +} + +func (k *KeyChainFile) SetSeed(seed cryptolib.Seed) error { + err := k.Set(seedKey, seed[:]) + return err +} + +func (k *KeyChainFile) GetSeed() (*cryptolib.Seed, error) { + seedItem, err := k.Get(seedKey) + if err != nil { + return nil, err + } + + if len(seedItem) != cryptolib.SeedSize { + return nil, ErrSeedDoesNotMatchLength + } + + seed := cryptolib.SeedFromBytes(seedItem) + return &seed, nil +} + +func (k *KeyChainFile) SetStrongholdPassword(password *memguard.Enclave) error { + buffer, err := password.Open() + if err != nil { + return err + } + defer buffer.Destroy() + + return k.Set(strongholdKey, buffer.Bytes()) +} + +func (k *KeyChainFile) GetStrongholdPassword() (*memguard.Enclave, error) { + seedItem, err := k.Get(strongholdKey) + if err != nil { + return nil, err + } + + return memguard.NewEnclave(seedItem), nil +} + +func (k *KeyChainFile) SetJWTAuthToken(node string, token string) error { + return k.Set(jwtTokenKey(node), []byte(token)) +} + +func (k *KeyChainFile) GetJWTAuthToken(node string) (string, error) { + seedItem, err := k.Get(jwtTokenKey(node)) + // Special case. If the key is not found, return an empty token. + if errors.Is(err, ErrKeyNotFound) { + return "", nil + } else if err != nil { + return "", err + } + + return string(seedItem), nil +} diff --git a/tools/wasp-cli/cli/keychain/keychain_file_test.go b/tools/wasp-cli/cli/keychain/keychain_file_test.go new file mode 100644 index 0000000000..af03d3ff3e --- /dev/null +++ b/tools/wasp-cli/cli/keychain/keychain_file_test.go @@ -0,0 +1,88 @@ +package keychain + +import ( + "os" + "testing" + + "github.com/awnumar/memguard" + "github.com/stretchr/testify/require" + + "github.com/iotaledger/wasp/packages/cryptolib" +) + +func TestGetSetSeed(t *testing.T) { + wd, _ := os.Getwd() + + keyChainFile := NewKeyChainFile(wd, func() *memguard.Enclave { + return memguard.NewEnclave([]byte("HI")) + }) + + testSeed := cryptolib.NewSeed() + err := keyChainFile.SetSeed(testSeed) + require.NoError(t, err) + + pulledSeed, err := keyChainFile.GetSeed() + require.NoError(t, err) + + require.EqualValues(t, pulledSeed, &testSeed) +} + +func TestGetSetJWT(t *testing.T) { + wd, _ := os.Getwd() + + keyChainFile := NewKeyChainFile(wd, func() *memguard.Enclave { + return memguard.NewEnclave([]byte("HI")) + }) + + testJWT := "ey....." + err := keyChainFile.SetJWTAuthToken("wasp", testJWT) + require.NoError(t, err) + + pulledSeed, err := keyChainFile.GetJWTAuthToken("wasp") + require.NoError(t, err) + + require.EqualValues(t, pulledSeed, testJWT) +} + +func TestGetSetStrongholdPassword(t *testing.T) { + wd, _ := os.Getwd() + + keyChainFile := NewKeyChainFile(wd, func() *memguard.Enclave { + return memguard.NewEnclave([]byte("HI")) + }) + + testStrongholdPassword := memguard.NewEnclaveRandom(32) + err := keyChainFile.SetStrongholdPassword(testStrongholdPassword) + require.NoError(t, err) + + pulledStrongholdPassword, err := keyChainFile.GetStrongholdPassword() + require.NoError(t, err) + + bufferA, err := testStrongholdPassword.Open() + require.NoError(t, err) + defer bufferA.Destroy() + + bufferB, err := pulledStrongholdPassword.Open() + require.NoError(t, err) + defer bufferB.Destroy() + + require.EqualValues(t, bufferA.Bytes(), bufferB.Bytes()) +} + +func TestInvalidPassword(t *testing.T) { + wd, _ := os.Getwd() + + keyChainFile := NewKeyChainFile(wd, func() *memguard.Enclave { + return memguard.NewEnclave([]byte("HI")) + }) + + err := keyChainFile.SetJWTAuthToken("wasp", "asd") + require.NoError(t, err) + + keyChainFile2 := NewKeyChainFile(wd, func() *memguard.Enclave { + return memguard.NewEnclave([]byte("WRONG_PW")) + }) + + _, err = keyChainFile2.GetJWTAuthToken("wasp") + require.ErrorIs(t, err, ErrInvalidPassword) +} diff --git a/tools/wasp-cli/cli/keychain/keychain_zalando.go b/tools/wasp-cli/cli/keychain/keychain_zalando.go new file mode 100644 index 0000000000..cac1de583d --- /dev/null +++ b/tools/wasp-cli/cli/keychain/keychain_zalando.go @@ -0,0 +1,94 @@ +package keychain + +import ( + "errors" + + "github.com/awnumar/memguard" + "github.com/zalando/go-keyring" + + iotago "github.com/iotaledger/iota.go/v3" + "github.com/iotaledger/wasp/packages/cryptolib" +) + +type KeyChainZalando struct{} + +func NewKeyChainZalando() *KeyChainZalando { + return &KeyChainZalando{} +} + +// IsKeyChainAvailable validates existence of a keychain by querying an entry. +// If a keychain is not available, it will throw an internal OS error, +// while an existing keychain will return ErrNotFound (as the key does not exist) +func IsKeyChainAvailable() bool { + _, err := keyring.Get("", "") + + if errors.Is(err, keyring.ErrNotFound) { + return true + } + + if errors.Is(err, keyring.ErrUnsupportedPlatform) { + return false + } + + return false +} + +func (k *KeyChainZalando) SetSeed(seed cryptolib.Seed) error { + err := keyring.Set(WaspCliServiceName, seedKey, iotago.EncodeHex(seed[:])) + return err +} + +func (k *KeyChainZalando) GetSeed() (*cryptolib.Seed, error) { + seedItem, err := keyring.Get(WaspCliServiceName, seedKey) + if errors.Is(err, keyring.ErrNotFound) { + return nil, ErrSeedDoesNotExist + } + if err != nil { + return nil, err + } + + seedBytes, err := iotago.DecodeHex(seedItem) + if err != nil { + return nil, err + } + + if len(seedBytes) != cryptolib.SeedSize { + return nil, ErrSeedDoesNotMatchLength + } + + seed := cryptolib.SeedFromBytes(seedBytes) + return &seed, nil +} + +func (k *KeyChainZalando) SetStrongholdPassword(password *memguard.Enclave) error { + buffer, err := password.Open() + if err != nil { + return err + } + defer buffer.Destroy() + + return keyring.Set(WaspCliServiceName, strongholdKey, buffer.String()) +} + +func (k *KeyChainZalando) GetStrongholdPassword() (*memguard.Enclave, error) { + seedItem, err := keyring.Get(WaspCliServiceName, strongholdKey) + if errors.Is(err, keyring.ErrNotFound) { + return nil, ErrPasswordDoesNotExist + } + + return memguard.NewEnclave([]byte(seedItem)), nil +} + +func (k *KeyChainZalando) SetJWTAuthToken(node string, token string) error { + return keyring.Set(WaspCliServiceName, jwtTokenKey(node), token) +} + +func (k *KeyChainZalando) GetJWTAuthToken(node string) (string, error) { + seedItem, err := keyring.Get(WaspCliServiceName, jwtTokenKey(node)) + // Special case. If the key is not found, return an empty token. + if errors.Is(err, keyring.ErrNotFound) { + return "", nil + } + + return seedItem, nil +} diff --git a/tools/wasp-cli/cli/password_entry.go b/tools/wasp-cli/cli/password_entry.go new file mode 100644 index 0000000000..b3a21df521 --- /dev/null +++ b/tools/wasp-cli/cli/password_entry.go @@ -0,0 +1,19 @@ +package cli + +import ( + "syscall" + + "github.com/awnumar/memguard" + "golang.org/x/term" + + "github.com/iotaledger/wasp/tools/wasp-cli/log" +) + +func ReadPasswordFromStdin() *memguard.Enclave { + log.Printf("\nPassword required to open/create secured storage.\n") + log.Printf("Password: ") + passwordBytes, err := term.ReadPassword(int(syscall.Stdin)) //nolint:nolintlint,unconvert // int cast is needed for windows + log.Check(err) + log.Printf("\n") + return memguard.NewEnclave(passwordBytes) +} diff --git a/tools/wasp-cli/cli/setup/init.go b/tools/wasp-cli/cli/setup/init.go index 413fa52d63..c48594bb11 100644 --- a/tools/wasp-cli/cli/setup/init.go +++ b/tools/wasp-cli/cli/setup/init.go @@ -20,7 +20,7 @@ func initRefreshL1ParamsCmd() *cobra.Command { } func Init(rootCmd *cobra.Command) { - rootCmd.PersistentFlags().StringVarP(&config.ConfigPath, "config", "c", "wasp-cli.json", "path to wasp-cli.json") + rootCmd.PersistentFlags().StringVarP(&config.ConfigPath, "config", "c", "", "path to wasp-cli.json") rootCmd.PersistentFlags().BoolVarP(&config.WaitForCompletion, "wait", "w", true, "wait for request completion") rootCmd.AddCommand(initCheckVersionsCmd()) diff --git a/tools/wasp-cli/cli/test/wallet_test.go b/tools/wasp-cli/cli/test/wallet_test.go new file mode 100644 index 0000000000..052868bef2 --- /dev/null +++ b/tools/wasp-cli/cli/test/wallet_test.go @@ -0,0 +1,49 @@ +package test + +import ( + "fmt" + "os" + "path" + "testing" + + "github.com/awnumar/memguard" + "github.com/stretchr/testify/require" + "github.com/tyler-smith/go-bip39" + + iotago "github.com/iotaledger/iota.go/v3" + wasp_wallet_sdk "github.com/iotaledger/wasp-wallet-sdk" + "github.com/iotaledger/wasp-wallet-sdk/types" + "github.com/iotaledger/wasp/packages/cryptolib" +) + +func TestMnemonic(t *testing.T) { + seedBytes, _ := iotago.DecodeHex("0xbc278147b72c6af948eced45252c496901e194c9610bfbffea639e18769c7715") + seed := cryptolib.SeedFromBytes(seedBytes) + kp := cryptolib.KeyPairFromSeed(seed) + address := kp.Address().Bech32("rms") + require.Equal(t, address, "rms1qzy0uqyzcm6asngsjxwc76nuar479uukvxa4dapzaz8fx5e3234wxw5mlmz") + fmt.Println(address) + + mnemonic, err := bip39.NewMnemonic(seed[:]) + require.NoError(t, err) + + cwd, err := os.Getwd() + require.NoError(t, err) + + sdk, err := wasp_wallet_sdk.NewIotaSDK(path.Join(cwd, "../../../../libiota_sdk_native.so")) + require.NoError(t, err) + + manager, err := wasp_wallet_sdk.NewStrongholdSecretManager(sdk, memguard.NewEnclaveRandom(32), "./test.snapshot") + defer os.Remove("./test.snapshot") + + require.NoError(t, err) + + mnemonicBytes := []byte(mnemonic) + success, err := manager.StoreMnemonic(memguard.NewEnclave(mnemonicBytes)) + require.NoError(t, err) + require.True(t, success) + + strongholdAddress, err := manager.GenerateEd25519Address(0, 0, "rms", types.CoinTypeSMR, nil) + require.NoError(t, err) + fmt.Println(strongholdAddress) +} diff --git a/tools/wasp-cli/cli/wallet/README.md b/tools/wasp-cli/cli/wallet/README.md new file mode 100644 index 0000000000..c3d274e29d --- /dev/null +++ b/tools/wasp-cli/cli/wallet/README.md @@ -0,0 +1,6 @@ +For the time being, two main ways for seed handling are implemented: + +* IOTA SDK based (Ledger, Stronghold - password in Keychain) +* Non IOTA SDK based (Seed in Keychain) + +As the IOTA SDK lib has not been used in production yet, it should be considered experimental. \ No newline at end of file diff --git a/tools/wasp-cli/cli/wallet/providers/keychain.go b/tools/wasp-cli/cli/wallet/providers/keychain.go new file mode 100644 index 0000000000..c9e4b8d209 --- /dev/null +++ b/tools/wasp-cli/cli/wallet/providers/keychain.go @@ -0,0 +1,48 @@ +package providers + +import ( + "github.com/iotaledger/wasp/packages/cryptolib" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/config" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/wallet/wallets" + "github.com/iotaledger/wasp/tools/wasp-cli/log" +) + +type KeyChainWallet struct { + cryptolib.VariantKeyPair + addressIndex uint32 +} + +func newInMemoryWallet(keyPair *cryptolib.KeyPair, addressIndex uint32) *KeyChainWallet { + return &KeyChainWallet{ + VariantKeyPair: keyPair, + addressIndex: addressIndex, + } +} + +func (i *KeyChainWallet) AddressIndex() uint32 { + return i.addressIndex +} + +func LoadKeyChain(addressIndex uint32) wallets.Wallet { + seed, err := config.GetKeyChain().GetSeed() + log.Check(err) + + useLegacyDerivation := config.GetUseLegacyDerivation() + keyPair := cryptolib.KeyPairFromSeed(cryptolib.SubSeed(seed[:], addressIndex, useLegacyDerivation)) + + return newInMemoryWallet(keyPair, addressIndex) +} + +func CreateKeyChain() { + seed := cryptolib.NewSeed() + err := config.GetKeyChain().SetSeed(seed) + log.Check(err) + + log.Printf("New seed stored in the keychain.\n") +} + +func MigrateKeyChain(seed cryptolib.Seed) { + err := config.GetKeyChain().SetSeed(seed) + log.Check(err) + log.Printf("Seed migrated to keychain") +} diff --git a/tools/wasp-cli/cli/wallet/providers/ledger.go b/tools/wasp-cli/cli/wallet/providers/ledger.go new file mode 100644 index 0000000000..4fcdc8dbc7 --- /dev/null +++ b/tools/wasp-cli/cli/wallet/providers/ledger.go @@ -0,0 +1,29 @@ +package providers + +import ( + walletsdk "github.com/iotaledger/wasp-wallet-sdk" + "github.com/iotaledger/wasp/packages/parameters" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/wallet/wallets" + "github.com/iotaledger/wasp/tools/wasp-cli/log" +) + +func LoadLedgerWallet(sdk *walletsdk.IOTASDK, addressIndex uint32) wallets.Wallet { + secretManager, err := walletsdk.NewLedgerSecretManager(sdk, false) + log.Check(err) + + status, err := secretManager.GetLedgerStatus() + log.Check(err) + + if !status.Connected { + log.Fatalf("Ledger could not be found.") + } + + if status.Locked { + log.Fatalf("Ledger is locked") + } + + hrp := parameters.L1().Protocol.Bech32HRP + coinType := MapCoinType(hrp) + + return wallets.NewExternalWallet(secretManager, addressIndex, string(hrp), coinType) +} diff --git a/tools/wasp-cli/cli/wallet/providers/mappers.go b/tools/wasp-cli/cli/wallet/providers/mappers.go new file mode 100644 index 0000000000..db7ce3a816 --- /dev/null +++ b/tools/wasp-cli/cli/wallet/providers/mappers.go @@ -0,0 +1,18 @@ +package providers + +import ( + iotago "github.com/iotaledger/iota.go/v3" + "github.com/iotaledger/wasp-wallet-sdk/types" +) + +func MapCoinType(prefix iotago.NetworkPrefix) types.CoinType { + switch prefix { + case iotago.PrefixMainnet, iotago.PrefixDevnet: + return types.CoinTypeIOTA + case iotago.PrefixShimmer, iotago.PrefixTestnet: + return types.CoinTypeSMR + default: + // For now returns SMR as default, but keep the logic above in case things change. + return types.CoinTypeSMR + } +} diff --git a/tools/wasp-cli/cli/wallet/providers/stronghold.go b/tools/wasp-cli/cli/wallet/providers/stronghold.go new file mode 100644 index 0000000000..d21bc9d025 --- /dev/null +++ b/tools/wasp-cli/cli/wallet/providers/stronghold.go @@ -0,0 +1,128 @@ +package providers + +import ( + "os" + "path" + "strings" + + "github.com/awnumar/memguard" + "github.com/tyler-smith/go-bip39" + + walletsdk "github.com/iotaledger/wasp-wallet-sdk" + "github.com/iotaledger/wasp/packages/cryptolib" + "github.com/iotaledger/wasp/packages/parameters" + "github.com/iotaledger/wasp/tools/wasp-cli/cli" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/config" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/wallet/wallets" + "github.com/iotaledger/wasp/tools/wasp-cli/log" +) + +func strongholdStorePath() string { + return path.Join(config.BaseDir, "client.stronghold") +} + +func strongholdStoreExists() bool { + _, err := os.Stat(strongholdStorePath()) + return err == nil +} + +func configureStronghold(sdk *walletsdk.IOTASDK, unlockPassword *memguard.Enclave) (*walletsdk.SecretManager, error) { + secretManager, err := walletsdk.NewStrongholdSecretManager(sdk, unlockPassword, strongholdStorePath()) + if err != nil { + return nil, err + } + + return secretManager, nil +} + +func LoadStrongholdWallet(sdk *walletsdk.IOTASDK, addressIndex uint32) wallets.Wallet { + password, err := config.GetKeyChain().GetStrongholdPassword() + log.Check(err) + + secretManager, err := configureStronghold(sdk, password) + log.Check(err) + + hrp := parameters.L1().Protocol.Bech32HRP + coinType := MapCoinType(hrp) + + return wallets.NewExternalWallet(secretManager, addressIndex, string(hrp), coinType) +} + +func printMnemonic(mnemonic *memguard.Enclave) { + buffer, err := mnemonic.Open() + log.Check(err) + defer buffer.Destroy() + mnemonicParts := strings.Split(buffer.String(), " ") + + for i, part := range mnemonicParts { + log.Printf("%s ", part) + if (i+1)%4 == 0 { + log.Printf("\n") + } + } +} + +func MigrateToStrongholdWallet(sdk *walletsdk.IOTASDK, seed cryptolib.Seed) { + log.Printf("Migrating existing seed into Stronghold store.\n\n") + + if strongholdStoreExists() { + log.Printf("There is an existing Stronghold store in '%s'\nIt will be overwritten once you enter a password.\n\n", strongholdStorePath()) + } + + log.Printf("Enter a secure password.\n") + unlockPassword := cli.ReadPasswordFromStdin() + log.Printf("\n") + + useLegacyDerivation := config.GetUseLegacyDerivation() + s := cryptolib.SubSeed(seed[:], 0, useLegacyDerivation) + + mnemonicStr, err := bip39.NewMnemonic(s[:]) + log.Check(err) + + mnemonic := memguard.NewEnclave([]byte(mnemonicStr)) + + createNewStrongholdWallet(sdk, mnemonic, unlockPassword) +} + +func createNewStrongholdWallet(sdk *walletsdk.IOTASDK, mnemonic *memguard.Enclave, password *memguard.Enclave) { + if strongholdStoreExists() { + err := os.Remove(strongholdStorePath()) + log.Check(err) + } + + log.Printf("\n\n") + + secretManager, err := configureStronghold(sdk, password) + log.Check(err) + + success, err := secretManager.StoreMnemonic(mnemonic) + log.Check(err) + + if success { + log.Printf("Stronghold store generated.\nWrite down the following mnemonic to recover your seed at a later time.\n\n") + printMnemonic(mnemonic) + } else { + log.Printf("Setting the mnemonic failed unexpectedly.") + return + } + + err = config.GetKeyChain().SetStrongholdPassword(password) + log.Check(err) +} + +func CreateNewStrongholdWallet(sdk *walletsdk.IOTASDK) { + log.Printf("Creating new Stronghold store.\n\n") + + if strongholdStoreExists() { + log.Printf("There is an existing Stronghold store in '%s'\nIt will be overwritten once you enter a password.\n\n", strongholdStorePath()) + } + + log.Printf("Enter a secure password.\n") + unlockPassword := cli.ReadPasswordFromStdin() + log.Printf("\n") + + mnemonic, err := sdk.Utils().GenerateMnemonic() + log.Check(err) + + createNewStrongholdWallet(sdk, mnemonic, unlockPassword) +} diff --git a/tools/wasp-cli/cli/wallet/wallet.go b/tools/wasp-cli/cli/wallet/wallet.go index 11e4ad33b0..4e0b0077f8 100644 --- a/tools/wasp-cli/cli/wallet/wallet.go +++ b/tools/wasp-cli/cli/wallet/wallet.go @@ -1,39 +1,154 @@ package wallet import ( - "github.com/spf13/viper" + "errors" + "fmt" + "os" + "path" + "path/filepath" + "runtime" iotago "github.com/iotaledger/iota.go/v3" + wasp_wallet_sdk "github.com/iotaledger/wasp-wallet-sdk" + "github.com/iotaledger/wasp-wallet-sdk/types" "github.com/iotaledger/wasp/packages/cryptolib" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/config" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/wallet/providers" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/wallet/wallets" "github.com/iotaledger/wasp/tools/wasp-cli/log" ) -var AddressIndex int +var AddressIndex uint32 -type Wallet struct { - KeyPair *cryptolib.KeyPair - AddressIndex int +type WalletProvider string + +const ( + ProviderKeyChain WalletProvider = "keychain" + ProviderLedger WalletProvider = "sdk_ledger" + ProviderStronghold WalletProvider = "sdk_stronghold" +) + +func GetWalletProvider() WalletProvider { + provider := WalletProvider(config.GetWalletProviderString()) + + switch provider { + case ProviderLedger, ProviderKeyChain, ProviderStronghold: + return provider + } + return ProviderKeyChain } -func Load() *Wallet { - seedHex := viper.GetString("wallet.seed") - useLegacyDerivation := viper.GetBool("wallet.useLegacyDerivation") - if seedHex == "" { - log.Fatal("call `init` first") +func SetWalletProvider(provider WalletProvider) error { + switch provider { + case ProviderLedger, ProviderKeyChain, ProviderStronghold: + config.SetWalletProviderString(string(provider)) + return nil } + return errors.New("invalid wallet provider provided") +} + +func getIotaSDKLibName() string { + switch runtime.GOOS { + case "windows": + return "iota_sdk.dll" + case "linux": + return "libiota_sdk.so" + case "darwin": + return "libiota_sdk.dylib" + default: + panic(fmt.Sprintf("unsupported OS: %s", runtime.GOOS)) + } +} - masterSeed, err := iotago.DecodeHex(seedHex) +func initIotaSDK(libPath string) *wasp_wallet_sdk.IOTASDK { + sdk, err := wasp_wallet_sdk.NewIotaSDK(libPath) log.Check(err) - kp := cryptolib.KeyPairFromSeed(cryptolib.SubSeed(masterSeed, uint32(AddressIndex), useLegacyDerivation)) + _, err = sdk.InitLogger(types.ILoggerConfig{ + LevelFilter: config.GetWalletLogLevel(), + }) + log.Check(err) - return &Wallet{KeyPair: kp, AddressIndex: AddressIndex} + return sdk } -func (w *Wallet) PrivateKey() *cryptolib.PrivateKey { - return w.KeyPair.GetPrivateKey() +func getIotaSDK() *wasp_wallet_sdk.IOTASDK { + // LoadLibrary (windows) and dlLoad (linux) have different search path behaviors + // For now, use a relative path - as it will eventually be shipped with a release. + // TODO: Revisit once proper release structure is set up. + + ex, err := os.Executable() + log.Check(err) + executableDir := filepath.Dir(ex) + + wd, err := os.Getwd() + log.Check(err) + + searchPaths := []string{ + path.Join(executableDir, getIotaSDKLibName()), + path.Join(wd, getIotaSDKLibName()), + } + + for _, searchPath := range searchPaths { + if _, err := os.Stat(searchPath); err == nil { + return initIotaSDK(searchPath) + } + } + + log.Fatalf("Could not find %v", getIotaSDKLibName()) + + return nil +} + +var loadedWallet wallets.Wallet + +func Load() wallets.Wallet { + walletProvider := GetWalletProvider() + + if loadedWallet == nil { + switch walletProvider { + case ProviderKeyChain: + loadedWallet = providers.LoadKeyChain(AddressIndex) + case ProviderLedger: + loadedWallet = providers.LoadLedgerWallet(getIotaSDK(), AddressIndex) + case ProviderStronghold: + loadedWallet = providers.LoadStrongholdWallet(getIotaSDK(), AddressIndex) + } + } + + return loadedWallet +} + +func InitWallet() { + walletProvider := GetWalletProvider() + + switch walletProvider { + case ProviderKeyChain: + providers.CreateKeyChain() + case ProviderLedger: + log.Printf("Ledger wallet provider selected, no initialization required") + case ProviderStronghold: + providers.CreateNewStrongholdWallet(getIotaSDK()) + } } -func (w *Wallet) Address() iotago.Address { - return w.KeyPair.GetPublicKey().AsEd25519Address() +func Migrate(provider WalletProvider) { + seedHex := config.GetSeedForMigration() + if seedHex == "" { + fmt.Println("No seed to migrate found.") + return + } + + seedBytes, err := iotago.DecodeHex(seedHex) + log.Check(err) + seed := cryptolib.SeedFromBytes(seedBytes) + + switch provider { + case ProviderKeyChain: + providers.MigrateKeyChain(seed) + case ProviderLedger: + log.Printf("Ledger wallet provider selected, no migration available") + case ProviderStronghold: + providers.MigrateToStrongholdWallet(getIotaSDK(), seed) + } } diff --git a/tools/wasp-cli/cli/wallet/wallets/converter.go b/tools/wasp-cli/cli/wallet/wallets/converter.go new file mode 100644 index 0000000000..b7978e79a5 --- /dev/null +++ b/tools/wasp-cli/cli/wallet/wallets/converter.go @@ -0,0 +1,24 @@ +package wallets + +import ( + iotago "github.com/iotaledger/iota.go/v3" + "github.com/iotaledger/wasp-wallet-sdk/types" +) + +func SDKED25519SignatureToIOTAGo(responseSignature *types.Ed25519Signature) (iotago.Signature, error) { + signatureBytes, err := iotago.DecodeHex(responseSignature.Signature) + if err != nil { + return nil, err + } + + publicKeyBytes, err := iotago.DecodeHex(responseSignature.PublicKey) + if err != nil { + return nil, err + } + + signature := iotago.Ed25519Signature{} + copy(signature.Signature[:], signatureBytes) + copy(signature.PublicKey[:], publicKeyBytes) + + return &signature, nil +} diff --git a/tools/wasp-cli/cli/wallet/wallets/external_address_signer.go b/tools/wasp-cli/cli/wallet/wallets/external_address_signer.go new file mode 100644 index 0000000000..b577406ad5 --- /dev/null +++ b/tools/wasp-cli/cli/wallet/wallets/external_address_signer.go @@ -0,0 +1,14 @@ +package wallets + +import ( + iotago "github.com/iotaledger/iota.go/v3" + "github.com/iotaledger/wasp/packages/cryptolib" +) + +type ExternalAddressSigner struct { + keyPair cryptolib.VariantKeyPair +} + +func (r *ExternalAddressSigner) Sign(addr iotago.Address, msg []byte) (signature iotago.Signature, err error) { + return r.keyPair.Sign(addr, msg) +} diff --git a/tools/wasp-cli/cli/wallet/wallets/external_wallet.go b/tools/wasp-cli/cli/wallet/wallets/external_wallet.go new file mode 100644 index 0000000000..570e8b0e77 --- /dev/null +++ b/tools/wasp-cli/cli/wallet/wallets/external_wallet.go @@ -0,0 +1,92 @@ +package wallets + +import ( + iotago "github.com/iotaledger/iota.go/v3" + walletsdk "github.com/iotaledger/wasp-wallet-sdk" + "github.com/iotaledger/wasp-wallet-sdk/types" + "github.com/iotaledger/wasp/packages/cryptolib" + "github.com/iotaledger/wasp/tools/wasp-cli/log" +) + +type ExternalWallet struct { + secretManager *walletsdk.SecretManager + + addressIndex uint32 + Bech32Hrp string + CoinType types.CoinType +} + +func NewExternalWallet(secretManager *walletsdk.SecretManager, addressIndex uint32, bech32Hrp string, coinType types.CoinType) *ExternalWallet { + return &ExternalWallet{ + secretManager: secretManager, + addressIndex: addressIndex, + Bech32Hrp: bech32Hrp, + CoinType: coinType, + } +} + +func (l *ExternalWallet) AddressIndex() uint32 { + return l.addressIndex +} + +func (l *ExternalWallet) Sign(addr iotago.Address, payload []byte) (signature iotago.Signature, err error) { + bip32Chain := walletsdk.BuildBip44Chain(l.CoinType, 0, l.addressIndex) + signResult, err := l.secretManager.SignTransactionEssence(types.HexEncodedString(iotago.EncodeHex(payload)), bip32Chain) + if err != nil { + return nil, err + } + + return SDKED25519SignatureToIOTAGo(signResult) +} + +func (l *ExternalWallet) SignBytes(payload []byte) []byte { + bip32Chain := walletsdk.BuildBip44Chain(l.CoinType, 0, l.addressIndex) + signResult, err := l.secretManager.SignTransactionEssence(types.HexEncodedString(iotago.EncodeHex(payload)), bip32Chain) + log.Check(err) + + signature, err := iotago.DecodeHex(signResult.Signature) + log.Check(err) + + return signature +} + +func (l *ExternalWallet) GetPublicKey() *cryptolib.PublicKey { + // To get the public key, it's required to sign some data first. + payload := make([]byte, 32) + signedPayload, err := l.Sign(nil, payload) + log.Check(err) + + ed25519Signature, ok := signedPayload.(*iotago.Ed25519Signature) + if !ok { + log.Fatalf("signed payload is not an ED25519 signature") + } + + publicKey, err := cryptolib.PublicKeyFromBytes(ed25519Signature.PublicKey[:]) + log.Check(err) + + return publicKey +} + +func (l *ExternalWallet) Address() *iotago.Ed25519Address { + addressStr, err := l.secretManager.GenerateEd25519Address(l.addressIndex, 0, l.Bech32Hrp, l.CoinType, &types.IGenerateAddressOptions{ + Internal: false, + LedgerNanoPrompt: false, + }) + log.Check(err) + + _, address, err := iotago.ParseBech32(addressStr) + log.Check(err) + + return address.(*iotago.Ed25519Address) +} + +func (l *ExternalWallet) AsAddressSigner() iotago.AddressSigner { + return &ExternalAddressSigner{ + keyPair: l, + } +} + +func (l *ExternalWallet) AddressKeysForEd25519Address(addr *iotago.Ed25519Address) iotago.AddressKeys { + // Not required, as we override the address signer above, and address keys are not used there. + return iotago.AddressKeys{} +} diff --git a/tools/wasp-cli/cli/wallet/wallets/wallet.go b/tools/wasp-cli/cli/wallet/wallets/wallet.go new file mode 100644 index 0000000000..9916fa299d --- /dev/null +++ b/tools/wasp-cli/cli/wallet/wallets/wallet.go @@ -0,0 +1,9 @@ +package wallets + +import "github.com/iotaledger/wasp/packages/cryptolib" + +type Wallet interface { + cryptolib.VariantKeyPair + + AddressIndex() uint32 +} diff --git a/tools/wasp-cli/go.mod b/tools/wasp-cli/go.mod index 37ee6cf8ec..c12c9a4166 100644 --- a/tools/wasp-cli/go.mod +++ b/tools/wasp-cli/go.mod @@ -9,21 +9,31 @@ replace ( ) require ( + github.com/99designs/keyring v1.2.2 + github.com/awnumar/memguard v0.22.4 + github.com/dvsekhvalnov/jose2go v1.6.0 github.com/ethereum/go-ethereum v1.13.4 github.com/hashicorp/go-version v1.6.0 github.com/iotaledger/hive.go/logger v0.0.0-20231106113411-94ac829adbb2 github.com/iotaledger/iota.go/v3 v3.0.0-rc.3 github.com/iotaledger/wasp v1.0.0-00010101000000-000000000000 + github.com/iotaledger/wasp-wallet-sdk v0.0.0-20240206105424-87cee27ddfbf github.com/samber/lo v1.38.1 github.com/spf13/cobra v1.8.0 github.com/spf13/viper v1.18.2 + github.com/stretchr/testify v1.8.4 + github.com/tyler-smith/go-bip39 v1.1.0 + github.com/zalando/go-keyring v0.2.3 golang.org/x/term v0.16.0 ) require ( filippo.io/edwards25519 v1.0.0 // indirect + github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect github.com/DataDog/zstd v1.5.5 // indirect github.com/VictoriaMetrics/fastcache v1.12.2 // indirect + github.com/alessio/shellescape v1.4.1 // indirect + github.com/awnumar/memcall v0.2.0 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.7.0 // indirect @@ -40,12 +50,14 @@ require ( github.com/containerd/cgroups v1.1.0 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/crate-crypto/go-kzg-4844 v0.3.0 // indirect + github.com/danieljoos/wincred v1.2.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect github.com/deckarep/golang-set/v2 v2.3.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect + github.com/ebitengine/purego v0.6.0-alpha.5 // indirect github.com/eclipse/paho.mqtt.golang v1.4.3 // indirect github.com/elastic/gosigar v0.14.2 // indirect github.com/ethereum/c-kzg-4844 v0.3.1 // indirect @@ -59,6 +71,8 @@ require ( github.com/go-ole/go-ole v1.3.0 // indirect github.com/go-stack/stack v1.8.1 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect @@ -75,6 +89,7 @@ require ( github.com/gorilla/websocket v1.5.1 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect + github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect github.com/hashicorp/hcl v1.0.0 // indirect github.com/holiman/bloomfilter/v2 v2.0.3 // indirect @@ -138,6 +153,7 @@ require ( github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/mmcloughlin/addchain v0.4.0 // indirect github.com/mr-tron/base58 v1.2.0 // indirect + github.com/mtibben/percent v0.2.1 // indirect github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multiaddr v0.12.2 // indirect @@ -180,14 +196,12 @@ require ( github.com/spf13/cast v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/status-im/keycard-go v0.2.0 // indirect - github.com/stretchr/testify v1.8.4 // indirect github.com/subosito/gotenv v1.6.0 // indirect github.com/supranational/blst v0.3.11 // indirect github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 // indirect github.com/tcnksm/go-latest v0.0.0-20170313132115-e3007ae9052e // indirect github.com/tklauser/go-sysconf v0.3.12 // indirect github.com/tklauser/numcpus v0.6.1 // indirect - github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect github.com/wasmerio/wasmer-go v1.0.4 // indirect @@ -206,7 +220,7 @@ require ( golang.org/x/mod v0.14.0 // indirect golang.org/x/net v0.20.0 // indirect golang.org/x/sync v0.6.0 // indirect - golang.org/x/sys v0.16.0 // indirect + golang.org/x/sys v0.17.0 // indirect golang.org/x/text v0.14.0 // indirect golang.org/x/time v0.5.0 // indirect golang.org/x/tools v0.17.0 // indirect diff --git a/tools/wasp-cli/go.sum b/tools/wasp-cli/go.sum index 269e5d4b28..c4c7fb9c3f 100644 --- a/tools/wasp-cli/go.sum +++ b/tools/wasp-cli/go.sum @@ -9,6 +9,10 @@ dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D filippo.io/edwards25519 v1.0.0 h1:0wAIcmJUqRdI8IJ/3eGi5/HwXZWPujYXXlkrQogz0Ek= filippo.io/edwards25519 v1.0.0/go.mod h1:N1IkdkCkiLB6tki+MYJoSx2JTY9NUlxZE7eHn5EwJns= git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 h1:/vQbFIOMbk2FiG/kXiLl8BRyzTWDw7gX/Hz7Dd5eDMs= +github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4/go.mod h1:hN7oaIRCjzsZ2dE+yG5k+rsdt3qcwykqK6HVGcKwsw4= +github.com/99designs/keyring v1.2.2 h1:pZd3neh/EmUzWONb35LxQfvuY7kiSXAq3HQd97+XBn0= +github.com/99designs/keyring v1.2.2/go.mod h1:wes/FrByc8j7lFOAGLGSNEg8f/PaI3cgTBqhFkHUrPk= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= @@ -21,6 +25,8 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0= +github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156 h1:eMwmnE/GDgah4HI848JfFxHt+iPb26b4zyfspmqY0/8= github.com/allegro/bigcache v1.2.1-0.20190218064605-e24eb225f156/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= @@ -29,6 +35,10 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/awnumar/memcall v0.2.0 h1:sRaogqExTOOkkNwO9pzJsL8jrOV29UuUW7teRMfbqtI= +github.com/awnumar/memcall v0.2.0/go.mod h1:S911igBPR9CThzd/hYQQmTc9SWNu3ZHIlCGaWsWsoJo= +github.com/awnumar/memguard v0.22.4 h1:1PLgKcgGPeExPHL8dCOWGVjIbQUBgJv9OL0F/yE1PqQ= +github.com/awnumar/memguard v0.22.4/go.mod h1:+APmZGThMBWjnMlKiSM1X7MVpbIVewen2MTkqWkA/zE= github.com/aws/aws-sdk-go-v2 v1.9.2/go.mod h1:cK/D0BBs0b/oWPIcX/Z/obahJK1TT7IPVjy53i/mX/4= github.com/aws/aws-sdk-go-v2/config v1.8.3/go.mod h1:4AEiLtAb8kLs7vgw2ZV3p2VZ1+hBavOc84hqxVNpCyw= github.com/aws/aws-sdk-go-v2/credentials v1.4.3/go.mod h1:FNNC6nQZQUuyhq5aE5c7ata8o9e4ECGmS4lAXC7o1mQ= @@ -104,6 +114,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/crate-crypto/go-kzg-4844 v0.3.0 h1:UBlWE0CgyFqqzTI+IFyCzA7A3Zw4iip6uzRv5NIXG0A= github.com/crate-crypto/go-kzg-4844 v0.3.0/go.mod h1:SBP7ikXEgDnUPONgm33HtuDZEDtWa3L4QtN1ocJSEQ4= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/danieljoos/wincred v1.2.0 h1:ozqKHaLK0W/ii4KVbbvluM91W2H3Sh0BncbUNPS7jLE= +github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= @@ -125,6 +137,10 @@ github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDD github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dvsekhvalnov/jose2go v1.6.0 h1:Y9gnSnP4qEI0+/uQkHvFXeD2PLPJeXEL+ySMEA2EjTY= +github.com/dvsekhvalnov/jose2go v1.6.0/go.mod h1:QsHjhyTlD/lAVqn/NSbVZmSCGeDehTB/mPZadG+mhXU= +github.com/ebitengine/purego v0.6.0-alpha.5 h1:EYID3JOAdmQ4SNZYJHu9V6IqOeRQDBYxqKAg9PyoHFY= +github.com/ebitengine/purego v0.6.0-alpha.5/go.mod h1:ah1In8AOtksoNK6yk5z1HTJeUkC1Ez4Wk2idgGslMwQ= github.com/eclipse/paho.mqtt.golang v1.4.3 h1:2kwcUGn8seMUfWndX0hGbvH8r7crgcJguQNCyp70xik= github.com/eclipse/paho.mqtt.golang v1.4.3/go.mod h1:CSYvoAlsMkhYOXh/oKyxa8EcBci6dVkLCbo5tTC1RIE= github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= @@ -181,6 +197,10 @@ github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/go-test/deep v1.0.2-0.20181118220953-042da051cf31/go.mod h1:wGDj63lr65AM2AQyKZd/NYHGb0R+1RLqB8NKt3aSFNA= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 h1:ZpnhV/YsD2/4cESfV5+Hoeu/iUR3ruzNvZ+yQfO03a0= +github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= @@ -263,6 +283,8 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= +github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542 h1:2VTzZjLZBgl62/EtslCrtky5vbi9dd7HrQPQIx6wqiw= github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI= github.com/hashicorp/consul/api v1.13.0/go.mod h1:ZlVrynguJKcYr54zGaDbaL3fOvKC9m72FhPvA8T35KQ= @@ -355,6 +377,8 @@ github.com/iotaledger/iota.go v1.0.0 h1:tqm1FxJ/zOdzbrAaQ5BQpVF8dUy2eeGlSeWlNG8G github.com/iotaledger/iota.go v1.0.0/go.mod h1:RiKYwDyY7aCD1L0YRzHSjOsJ5mUR9yvQpvhZncNcGQI= github.com/iotaledger/iota.go/v3 v3.0.0-rc.3 h1:/2gH+AlsWX2T7f4uHvD7oAHmPRsW+SkjR29txzqpi5M= github.com/iotaledger/iota.go/v3 v3.0.0-rc.3/go.mod h1:R3m6d5AFI0I++HOaYQR7QU8hY//vUSwbOyqQ7Bco2O4= +github.com/iotaledger/wasp-wallet-sdk v0.0.0-20240206105424-87cee27ddfbf h1:OpWBa6XYL0CvRYNqjfa/glctDgVFTVAxJSd5hum7PS4= +github.com/iotaledger/wasp-wallet-sdk v0.0.0-20240206105424-87cee27ddfbf/go.mod h1:QJt/KZdzzcqSql+CMWIc9KBjkUN3hK4iQrXCS9/2tzs= github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= @@ -510,6 +534,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mtibben/percent v0.2.1 h1:5gssi8Nqo8QU/r2pynCm+hBQHpkB/uNK7BJCFogWdzs= +github.com/mtibben/percent v0.2.1/go.mod h1:KG9uO+SZkUp+VkRHsCdYQV3XSZrrSpR3O9ibNBTZrns= github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0= @@ -539,6 +565,7 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/npillmayer/nestext v0.1.3/go.mod h1:h2lrijH8jpicr25dFY+oAJLyzlya6jhnuG+zWp9L0Uk= github.com/nxadm/tail v1.4.4 h1:DQuhQpB1tVlglWS2hLQ5OV6B5r8aGxSrPc5Qo6uTN78= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= @@ -753,6 +780,8 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +github.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms= +github.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk= go.dedis.ch/fixbuf v1.0.3 h1:hGcV9Cd/znUxlusJ64eAlExS+5cJDIyTyEG+otu5wQs= go.dedis.ch/fixbuf v1.0.3/go.mod h1:yzJMt34Wa5xD37V5RTdmp38cz3QhMagdGoem9anUalw= go.dedis.ch/protobuf v1.0.11 h1:FTYVIEzY/bfl37lu3pR4lIj+F9Vp1jE8oh91VmxKgLo= @@ -878,6 +907,7 @@ golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191008105621-543471e840be/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -911,8 +941,8 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= @@ -1004,6 +1034,7 @@ gopkg.in/asn1-ber.v1 v1.0.0-20181015200546-f715ec2f112d/go.mod h1:cuepJuh7vyXfUy gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= diff --git a/tools/wasp-cli/wallet/cmd.go b/tools/wasp-cli/wallet/cmd.go index a9189a2e8a..8ed2622bc8 100644 --- a/tools/wasp-cli/wallet/cmd.go +++ b/tools/wasp-cli/wallet/cmd.go @@ -12,6 +12,8 @@ func Init(rootCmd *cobra.Command) { rootCmd.AddCommand(initBalanceCmd()) rootCmd.AddCommand(initSendFundsCmd()) rootCmd.AddCommand(initRequestFundsCmd()) + rootCmd.AddCommand(initWalletProviderCmd()) + rootCmd.AddCommand(initMigrateCmd()) - rootCmd.PersistentFlags().IntVarP(&wallet.AddressIndex, "address-index", "i", 0, "address index") + rootCmd.PersistentFlags().Uint32VarP(&wallet.AddressIndex, "address-index", "i", 0, "address index") } diff --git a/tools/wasp-cli/wallet/info.go b/tools/wasp-cli/wallet/info.go index 6c7761f8a3..48d05860c7 100644 --- a/tools/wasp-cli/wallet/info.go +++ b/tools/wasp-cli/wallet/info.go @@ -20,12 +20,12 @@ func initAddressCmd() *cobra.Command { myWallet := wallet.Load() address := myWallet.Address() - model := &AddressModel{Address: address.Bech32(parameters.L1().Protocol.Bech32HRP)} + model := &AddressModel{Address: address.Bech32(parameters.L1().Protocol.Bech32HRP), Index: int(myWallet.AddressIndex())} if log.VerboseFlag { verboseOutput := make(map[string]string) - verboseOutput["Private key"] = myWallet.KeyPair.GetPrivateKey().String() - verboseOutput["Public key"] = myWallet.KeyPair.GetPublicKey().String() + // verboseOutput["Private key"] = myWallet.KeyPair.GetPrivateKey().String() + verboseOutput["Public key"] = myWallet.GetPublicKey().String() model.VerboseOutput = verboseOutput } log.PrintCLIOutput(model) @@ -68,7 +68,7 @@ func initBalanceCmd() *cobra.Command { model := &BalanceModel{ Address: address.Bech32(parameters.L1().Protocol.Bech32HRP), - AddressIndex: myWallet.AddressIndex, + AddressIndex: myWallet.AddressIndex(), NativeTokens: balance.NativeTokens, BaseTokens: balance.BaseTokens, OutputMap: outs, @@ -90,7 +90,7 @@ func initBalanceCmd() *cobra.Command { var _ log.CLIOutput = &BalanceModel{} type BalanceModel struct { - AddressIndex int `json:"AddressIndex"` + AddressIndex uint32 `json:"AddressIndex"` Address string `json:"Address"` BaseTokens uint64 `json:"BaseTokens"` NativeTokens iotago.NativeTokens `json:"NativeTokens"` diff --git a/tools/wasp-cli/wallet/migrate.go b/tools/wasp-cli/wallet/migrate.go new file mode 100644 index 0000000000..70b674b5d8 --- /dev/null +++ b/tools/wasp-cli/wallet/migrate.go @@ -0,0 +1,18 @@ +package wallet + +import ( + "github.com/spf13/cobra" + + "github.com/iotaledger/wasp/tools/wasp-cli/cli/wallet" +) + +func initMigrateCmd() *cobra.Command { + return &cobra.Command{ + Use: "wallet-migrate (keychain)", + Short: "Migrates a seed inside `wasp-cli.json` to the keychain provider", + Args: cobra.ExactArgs(1), + Run: func(cmd *cobra.Command, args []string) { + wallet.Migrate(wallet.WalletProvider(args[0])) + }, + } +} diff --git a/tools/wasp-cli/wallet/send.go b/tools/wasp-cli/wallet/send.go index 01ae79b0f1..4b9b336046 100644 --- a/tools/wasp-cli/wallet/send.go +++ b/tools/wasp-cli/wallet/send.go @@ -52,7 +52,7 @@ func initSendFundsCmd() *cobra.Command { FungibleTokens: tokens, SendOptions: isc.SendOptions{}, SenderAddress: senderAddress, - SenderKeyPair: myWallet.KeyPair, + SenderKeyPair: myWallet, TargetAddress: targetAddress, UnspentOutputs: outputSet, UnspentOutputIDs: isc.OutputSetToOutputIDs(outputSet), diff --git a/tools/wasp-cli/wallet/wallet.go b/tools/wasp-cli/wallet/wallet.go index 931529d49b..76032fa102 100644 --- a/tools/wasp-cli/wallet/wallet.go +++ b/tools/wasp-cli/wallet/wallet.go @@ -4,14 +4,14 @@ import ( "github.com/spf13/cobra" "github.com/spf13/viper" - iotago "github.com/iotaledger/iota.go/v3" "github.com/iotaledger/wasp/packages/cryptolib" "github.com/iotaledger/wasp/tools/wasp-cli/cli/config" + "github.com/iotaledger/wasp/tools/wasp-cli/cli/wallet" "github.com/iotaledger/wasp/tools/wasp-cli/log" ) type WalletConfig struct { - Seed []byte + KeyPair cryptolib.VariantKeyPair } func initInitCmd() *cobra.Command { @@ -20,36 +20,21 @@ func initInitCmd() *cobra.Command { Short: "Initialize a new wallet", Args: cobra.NoArgs, Run: func(cmd *cobra.Command, args []string) { - seed := cryptolib.NewSeed() - seedString := iotago.EncodeHex(seed[:]) - viper.Set("wallet.seed", seedString) - log.Check(viper.WriteConfig()) + wallet.InitWallet() - model := &InitModel{ - ConfigPath: config.ConfigPath, - } - if log.VerboseFlag { - model.Seed = seedString - } - log.PrintCLIOutput(model) + config.SetWalletProviderString(string(wallet.GetWalletProvider())) + log.Check(viper.WriteConfig()) }, } } type InitModel struct { - ConfigPath string - Seed string + Scheme string } var _ log.CLIOutput = &InitModel{} func (i *InitModel) AsText() (string, error) { - template := `Initialized wallet seed in {{ .ConfigPath }} - -IMPORTANT: wasp-cli is alpha phase. The seed is currently being stored in a plain text file which is NOT secure. Do not use this seed to store funds in the mainnet. -{{ if .Seed }} - Seed: {{ .Seed -}} -{{ end }} -` + template := `Initialized wallet seed in {{ .Scheme }}` return log.ParseCLIOutputTemplate(i, template) } diff --git a/tools/wasp-cli/wallet/wallet_provider.go b/tools/wasp-cli/wallet/wallet_provider.go new file mode 100644 index 0000000000..d8cf2d51ac --- /dev/null +++ b/tools/wasp-cli/wallet/wallet_provider.go @@ -0,0 +1,24 @@ +package wallet + +import ( + "github.com/spf13/cobra" + + "github.com/iotaledger/wasp/tools/wasp-cli/cli/wallet" + "github.com/iotaledger/wasp/tools/wasp-cli/log" +) + +func initWalletProviderCmd() *cobra.Command { + return &cobra.Command{ + Use: "wallet-provider (keychain, sdk_ledger, sdk_stronghold)", + Short: "Get or set wallet provider (keychain, sdk_ledger, sdk_stronghold)", + Args: cobra.MaximumNArgs(1), + Run: func(cmd *cobra.Command, args []string) { + if len(args) == 0 { + log.Printf("Wallet provider: %s\n", string(wallet.GetWalletProvider())) + return + } + + log.Check(wallet.SetWalletProvider(wallet.WalletProvider(args[0]))) + }, + } +}