├── .cargo └── config.toml ├── .ci ├── pushdocs.yml ├── release.yml ├── test.yml ├── win32_cargo.bat ├── win64_cargo.bat └── windows-release.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── cd.yaml │ └── ci.yaml ├── .gitignore ├── .hooks └── pre-commit ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── api ├── Cargo.toml └── src │ ├── foreign.rs │ ├── foreign_rpc.rs │ ├── lib.rs │ ├── owner.rs │ ├── owner_rpc_v2.rs │ ├── owner_rpc_v3.rs │ └── types.rs ├── azure-pipelines.yml ├── build_static.sh ├── build_static32.bat ├── build_static64.bat ├── build_static_linux.sh ├── config ├── Cargo.toml └── src │ ├── comments.rs │ ├── config.rs │ ├── lib.rs │ └── types.rs ├── controller ├── Cargo.toml ├── src │ ├── command.rs │ ├── controller.rs │ ├── display.rs │ ├── error.rs │ ├── executor.rs │ └── lib.rs └── tests │ ├── accounts.rs │ ├── broken_change.rs │ ├── build_chain.rs │ ├── build_output.rs │ ├── check.rs │ ├── common │ └── mod.rs │ ├── file.rs │ ├── integrity_kernel.rs │ ├── invoice.rs │ ├── late_lock.rs │ ├── no_change.rs │ ├── ownership_proofs.rs │ ├── payment_proofs.rs │ ├── repost.rs │ ├── revert.rs │ ├── self_send.rs │ ├── self_spend.rs │ ├── slatepack.rs │ ├── transaction.rs │ ├── ttl_cutoff.rs │ ├── tx_list_filter.rs │ └── updater_thread.rs ├── doc ├── atomic_swaps.md ├── change_outputs.md ├── design │ ├── design.md │ ├── goals.md │ ├── wallet-arch.png │ └── wallet-arch.puml ├── floonet_faucet.md ├── installing_openssl.md ├── samples │ └── v3_api_node │ │ ├── package-lock.json │ │ ├── package.json │ │ ├── readme.md │ │ └── src │ │ └── index.js ├── setup.md ├── setup_other_currency_nodes.md ├── slatepack_data_format.md ├── swap │ └── electrumx_install.md ├── tls-setup.md ├── tor_for_exchanges_pools.md └── transaction │ ├── basic-transaction-wf.puml │ └── mwc-transaction-wf.png ├── file ├── impls ├── Cargo.toml └── src │ ├── adapters │ ├── file.rs │ ├── http.rs │ ├── libp2p_messaging.rs │ ├── mod.rs │ ├── mwcmq.rs │ └── types.rs │ ├── backends │ ├── lmdb.rs │ └── mod.rs │ ├── client_utils │ ├── client.rs │ ├── json_rpc.rs │ ├── mod.rs │ └── socksv5.rs │ ├── error.rs │ ├── lib.rs │ ├── lifecycle │ ├── default.rs │ ├── mod.rs │ └── seed.rs │ ├── node_clients │ ├── http.rs │ ├── mod.rs │ └── resp_types.rs │ ├── test_framework │ ├── mod.rs │ └── testclient.rs │ └── tor │ ├── bridge.rs │ ├── config.rs │ ├── mod.rs │ ├── process.rs │ ├── proxy.rs │ └── status.rs ├── integration ├── Cargo.toml ├── src │ └── lib.rs └── tests │ ├── api.rs │ ├── dandelion.rs │ ├── framework.rs │ ├── simulnet.rs │ └── stratum.rs ├── libwallet ├── Cargo.toml ├── src │ ├── address.rs │ ├── api_impl.rs │ ├── api_impl │ │ ├── foreign.rs │ │ ├── owner.rs │ │ ├── owner_eth.rs │ │ ├── owner_libp2p.rs │ │ ├── owner_swap.rs │ │ ├── owner_updater.rs │ │ └── types.rs │ ├── error.rs │ ├── internal.rs │ ├── internal │ │ ├── keys.rs │ │ ├── scan.rs │ │ ├── selection.rs │ │ ├── tx.rs │ │ └── updater.rs │ ├── lib.rs │ ├── proof │ │ ├── base58.rs │ │ ├── crypto.rs │ │ ├── hasher.rs │ │ ├── message.rs │ │ ├── mod.rs │ │ ├── proofaddress.rs │ │ └── tx_proof.rs │ ├── slate.rs │ ├── slate_versions │ │ ├── mod.rs │ │ ├── ser.rs │ │ ├── v2.rs │ │ └── v3.rs │ ├── slatepack │ │ ├── armor.rs │ │ ├── mod.rs │ │ ├── packer.rs │ │ └── slatepack.rs │ ├── swap │ │ ├── api.rs │ │ ├── bitcoin │ │ │ ├── api.rs │ │ │ ├── client.rs │ │ │ ├── electrum.rs │ │ │ ├── mod.rs │ │ │ ├── rpc.rs │ │ │ └── types.rs │ │ ├── buyer.rs │ │ ├── error.rs │ │ ├── ethereum │ │ │ ├── api.rs │ │ │ ├── client.rs │ │ │ ├── decimal_convert.rs │ │ │ ├── erc20_contract.rs │ │ │ ├── erc20_swap_contract.rs │ │ │ ├── ethereum.rs │ │ │ ├── infura.rs │ │ │ ├── mod.rs │ │ │ ├── swap_contract.rs │ │ │ └── types.rs │ │ ├── fsm │ │ │ ├── buyer_swap.rs │ │ │ ├── machine.rs │ │ │ ├── mod.rs │ │ │ ├── seller_swap.rs │ │ │ └── state.rs │ │ ├── message.rs │ │ ├── mod.rs │ │ ├── multisig │ │ │ ├── error.rs │ │ │ ├── mod.rs │ │ │ └── types.rs │ │ ├── seller.rs │ │ ├── ser.rs │ │ ├── swap.rs │ │ ├── trades.rs │ │ └── types.rs │ └── types.rs ├── swap_test │ ├── context_buy.json │ ├── context_sell.json │ ├── message_1.json │ ├── message_2.json │ ├── message_3.json │ ├── message_4.json │ ├── swap_buy_1.json │ ├── swap_buy_2.json │ ├── swap_buy_3.json │ ├── swap_sell_1.json │ ├── swap_sell_2.json │ ├── swap_sell_3.json │ ├── swap_sell_4.json │ ├── swap_sell_5.json │ └── swap_sell_6.json └── tests │ ├── libwallet.rs │ ├── slate_versioning.rs │ └── slates │ └── v2.slate ├── mypatch.patch ├── rustfmt.toml ├── src ├── bin │ ├── mwc-wallet.rs │ └── mwc-wallet.yml ├── build │ └── build.rs ├── cli │ ├── cli.rs │ └── mod.rs ├── cmd │ ├── mod.rs │ ├── wallet.rs │ └── wallet_args.rs └── lib.rs ├── tests ├── cmd_line_basic.rs ├── common │ └── mod.rs ├── data │ ├── v2_reqs │ │ ├── init_send_tx.req.json │ │ └── retrieve_info.req.json │ └── v3_reqs │ │ ├── change_password.req.json │ │ ├── close_wallet.req.json │ │ ├── create_config.req.json │ │ ├── create_wallet.req.json │ │ ├── create_wallet_invalid_mn.req.json │ │ ├── create_wallet_valid_mn.req.json │ │ ├── delete_wallet.req.json │ │ ├── get_top_level.req.json │ │ ├── init_secure_api.req.json │ │ ├── init_send_tx.req.json │ │ ├── open_wallet.req.json │ │ └── retrieve_info.req.json ├── owner_v2_sanity.rs ├── owner_v3_init_secure.rs ├── owner_v3_lifecycle.rs └── tor_dev_helper.rs └── util ├── Cargo.toml └── src ├── lib.rs ├── ov3.rs └── tokio_runtime.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.x86_64-pc-windows-msvc] 2 | rustflags = ["-Ctarget-feature=+crt-static"] 3 | [target.i686-pc-windows-msvc] 4 | rustflags = ["-Ctarget-feature=+crt-static"] 5 | -------------------------------------------------------------------------------- /.ci/pushdocs.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: | 3 | cd /home/vsts/work/1/s/api && cargo fetch && cargo doc --offline --no-deps 4 | mkdir /home/vsts/work/1/gitpages && cd /home/vsts/work/1/gitpages && git clone https://anything:$(github_pat)@github.com/$(ghpages_user)/$(ghpages_repo).git . 5 | 6 | cd /home/vsts/work/1/gitpages 7 | git config user.name $(ghpages_user) 8 | git checkout master 9 | cp -a /home/vsts/work/1/s/target/doc/* /home/vsts/work/1/gitpages/ 10 | echo '' > /home/vsts/work/1/gitpages/index.html && \ 11 | git add --all 12 | git commit -m"Pipelines-Bot: Updated site via $(Build.SourceVersion)"; 13 | git push https://$(github_pat)@github.com/mwcproject/mwcproject.github.io.git 14 | 15 | curl https://api.github.com/repos/$(ghpages_user)/$(ghpages_repo)/pages/builds/latest -i -v \ 16 | -X GET \ 17 | -H "Accept: application/vnd.github.mister-fantastic-preview+json" \ 18 | -H "Authorization: Basic $(ghpages_auth_header)" 19 | displayName: 'Create and Push Docs' 20 | condition: succeeded() 21 | -------------------------------------------------------------------------------- /.ci/release.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: 'cargo test --all' 3 | displayName: Cargo Test All 4 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 5 | - script: './build_static_linux.sh' 6 | displayName: Build Linux Release 7 | condition: and(succeeded(), eq( variables['Agent.OS'], 'Linux' )) 8 | - script: './build_static.sh' 9 | displayName: Build MacOS Release 10 | condition: and(succeeded(), eq( variables['Agent.OS'], 'Darwin' )) 11 | - script: | 12 | MY_TAG="$(Build.SourceBranch)" 13 | MY_TAG=${MY_TAG#refs/tags/} 14 | echo $MY_TAG 15 | echo "##vso[task.setvariable variable=build.my_tag]$MY_TAG" 16 | echo "##vso[task.setvariable variable=build.platform]$PLATFORM" 17 | displayName: "Create my tag variable" 18 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 19 | - task: CopyFiles@2 20 | displayName: Copy assets 21 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 22 | inputs: 23 | sourceFolder: '$(Build.SourcesDirectory)/target/release' 24 | contents: 'mwc-wallet' 25 | targetFolder: '$(Build.BinariesDirectory)/mwc-wallet' 26 | - task: ArchiveFiles@2 27 | displayName: Gather assets 28 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 29 | inputs: 30 | rootFolderOrFile: '$(Build.BinariesDirectory)/mwc-wallet' 31 | archiveType: 'tar' 32 | tarCompression: 'gz' 33 | archiveFile: '$(Build.ArtifactStagingDirectory)/mwc-wallet-$(build.my_tag)-$(build.platform).tar.gz' 34 | - script: | 35 | cd $(Build.ArtifactStagingDirectory) && openssl sha256 mwc-wallet-$(build.my_tag)-$(build.platform).tar.gz > mwc-wallet-$(build.my_tag)-$(build.platform)-sha256sum.txt 36 | displayName: Create Checksum 37 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 38 | - task: GithubRelease@0 39 | displayName: Github release 40 | condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 41 | inputs: 42 | gitHubConnection: 'github.com_vekamo' 43 | repositoryName: 'mwcproject/mwc-wallet' 44 | action: 'edit' 45 | tagSource: "Git tag" 46 | tag: '$(build.my_tag)' 47 | assets: | 48 | $(Build.ArtifactStagingDirectory)/mwc-wallet-$(build.my_tag)-$(build.platform).tar.gz 49 | $(Build.ArtifactStagingDirectory)/mwc-wallet-$(build.my_tag)-$(build.platform)-sha256sum.txt 50 | title: '$(build.my_tag)' 51 | assetUploadMode: 'replace' 52 | addChangeLog: true -------------------------------------------------------------------------------- /.ci/test.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: '.ci\win64_cargo.bat test --all' 3 | env: 4 | LIBCLANG_PATH: C:\Program Files\LLVM\lib 5 | LLVM_CONFIG_PATH: C:\Program Files\LLVM\bin\llvm-config 6 | ROARING_ARCH: x86-64-v2 7 | displayName: Windows Cargo Test 8 | condition: and(eq( variables['Agent.OS'], 'Windows_NT' ), eq( variables['CI_JOB'], 'test-all' )) 9 | - script: 'ROARING_ARCH=x86-64-v2 cargo test --all' 10 | displayName: macOS Cargo Test 11 | condition: and(eq( variables['Agent.OS'], 'Darwin' ), eq( variables['CI_JOB'], 'test-all' )) 12 | - script: 'ROARING_ARCH=x86-64-v2 cargo test --all' 13 | displayName: Linux Cargo Test 14 | condition: and(eq( variables['Agent.OS'], 'Linux' ), eq( variables['CI_JOB'], 'test-all' )) 15 | -------------------------------------------------------------------------------- /.ci/win32_cargo.bat: -------------------------------------------------------------------------------- 1 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars32.bat" 2 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars32.bat" 3 | 4 | cargo %1 %2 %3 %4 %5 %6 %7 5 | -------------------------------------------------------------------------------- /.ci/win64_cargo.bat: -------------------------------------------------------------------------------- 1 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\VC\Auxiliary\Build\vcvars64.bat" 2 | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Auxiliary\Build\vcvars64.bat" 3 | 4 | cargo %1 %2 %3 %4 %5 %6 %7 5 | -------------------------------------------------------------------------------- /.ci/windows-release.yml: -------------------------------------------------------------------------------- 1 | steps: 2 | - script: '.ci\win64_cargo.bat test --all' 3 | env: 4 | LIBCLANG_PATH: C:\Program Files\LLVM\lib 5 | LLVM_CONFIG_PATH: C:\Program Files\LLVM\bin\llvm-config 6 | ROARING_ARCH: x86-64-v2 7 | displayName: Cargo Test All 8 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 9 | - script: 'build_static64.bat' 10 | env: 11 | LIBCLANG_PATH: C:\Program Files\LLVM\lib 12 | LLVM_CONFIG_PATH: C:\Program Files\LLVM\bin\llvm-config 13 | ROARING_ARCH: x86-64-v2 14 | displayName: Build Release 15 | - script: | 16 | SET MY_TAG=$(Build.SourceBranch) 17 | SET MY_TAG=%MY_TAG:~10% 18 | echo %MY_TAG% 19 | echo %PLATFORM% 20 | echo ##vso[task.setvariable variable=build.my_tag]%MY_TAG% 21 | echo ##vso[task.setvariable variable=build.platform]%PLATFORM% 22 | displayName: "Create my tag variable" 23 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 24 | - task: CopyFiles@2 25 | displayName: Copy assets 26 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 27 | inputs: 28 | sourceFolder: '$(Build.SourcesDirectory)\target\release' 29 | contents: 'mwc-wallet.exe' 30 | targetFolder: '$(Build.BinariesDirectory)\mwc-wallet' 31 | - task: ArchiveFiles@2 32 | displayName: Gather assets 33 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 34 | inputs: 35 | rootFolderOrFile: '$(Build.BinariesDirectory)\mwc-wallet' 36 | archiveType: 'zip' 37 | archiveFile: '$(Build.ArtifactStagingDirectory)\mwc-wallet-$(build.my_tag)-$(build.platform).zip' 38 | - script: | 39 | powershell -Command "cd $(Build.ArtifactStagingDirectory); get-filehash -algorithm sha256 mwc-wallet-$(build.my_tag)-$(build.platform).zip | Format-List |  Out-String | ForEach-Object { $_.Trim() } > mwc-wallet-$(build.my_tag)-$(build.platform)-sha256sum.txt" 40 | displayName: Create Checksum 41 | condition: and(succeeded(), contains(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 42 | - task: GithubRelease@0 43 | displayName: Github release 44 | condition: and(succeeded(), startsWith(variables['Build.SourceBranch'], 'refs/tags/'), eq(variables['CI_JOB'], 'release' )) 45 | inputs: 46 | gitHubConnection: 'github.com_vekamo' 47 | repositoryName: 'mwcproject/mwc-wallet' 48 | action: 'edit' 49 | tagSource: "Git tag" 50 | tag: '$(build.my_tag)' 51 | assets: | 52 | $(Build.ArtifactStagingDirectory)\mwc-wallet-$(build.my_tag)-$(build.platform).zip 53 | $(Build.ArtifactStagingDirectory)\mwc-wallet-$(build.my_tag)-$(build.platform)-sha256sum.txt 54 | title: '$(build.my_tag)' 55 | assetUploadMode: 'replace' 56 | addChangeLog: true 57 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots** 24 | If applicable, add screenshots to help explain your problem. 25 | 26 | **Desktop (please complete the following information):** 27 | - OS: [e.g. iOS] 28 | - Version [e.g. 22] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.github/workflows/cd.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Deployment 2 | 3 | on: 4 | push: 5 | tags: 6 | - "v*.*.*" 7 | 8 | jobs: 9 | linux-release: 10 | name: Linux Release 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v3 14 | - name: Build 15 | run: cargo build --release 16 | - name: Archive 17 | working-directory: target/release 18 | run: tar -czvf grin-wallet-${{ github.ref_name }}-linux-x86_64.tar.gz grin-wallet 19 | - name: Create Checksum 20 | working-directory: target/release 21 | run: openssl sha256 grin-wallet-${{ github.ref_name }}-linux-x86_64.tar.gz > grin-wallet-${{ github.ref_name }}-linux-x86_64-sha256sum.txt 22 | - name: Release 23 | uses: softprops/action-gh-release@v1 24 | with: 25 | generate_release_notes: true 26 | files: | 27 | target/release/grin-wallet-${{ github.ref_name }}-linux-x86_64.tar.gz 28 | target/release/grin-wallet-${{ github.ref_name }}-linux-x86_64-sha256sum.txt 29 | 30 | macos-release: 31 | name: macOS Release 32 | runs-on: macos-latest 33 | steps: 34 | - name: Checkout 35 | uses: actions/checkout@v3 36 | - name: Build 37 | run: cargo build --release 38 | - name: Archive 39 | working-directory: target/release 40 | run: tar -czvf grin-wallet-${{ github.ref_name }}-macos-x86_64.tar.gz grin-wallet 41 | - name: Create Checksum 42 | working-directory: target/release 43 | run: openssl sha256 grin-wallet-${{ github.ref_name }}-macos-x86_64.tar.gz > grin-wallet-${{ github.ref_name }}-macos-x86_64-sha256sum.txt 44 | - name: Release 45 | uses: softprops/action-gh-release@v1 46 | with: 47 | files: | 48 | target/release/grin-wallet-${{ github.ref_name }}-macos-x86_64.tar.gz 49 | target/release/grin-wallet-${{ github.ref_name }}-macos-x86_64-sha256sum.txt 50 | 51 | windows-release: 52 | name: Windows Release 53 | runs-on: windows-latest 54 | steps: 55 | - name: Checkout 56 | uses: actions/checkout@v3 57 | - name: Build 58 | run: cargo build --release 59 | - name: Archive 60 | uses: vimtor/action-zip@v1 61 | with: 62 | files: target/release/grin-wallet.exe 63 | dest: target/release/grin-wallet-${{ github.ref_name }}-win-x86_64.zip 64 | - name: Create Checksum 65 | working-directory: target/release 66 | shell: pwsh 67 | run: get-filehash -algorithm sha256 grin-wallet-${{ github.ref_name }}-win-x86_64.zip | Format-List |  Out-String | ForEach-Object { $_.Trim() } > grin-wallet-${{ github.ref_name }}-win-x86_64-sha256sum.txt 68 | - name: Release 69 | uses: softprops/action-gh-release@v1 70 | with: 71 | files: | 72 | target/release/grin-wallet-${{ github.ref_name }}-win-x86_64.zip 73 | target/release/grin-wallet-${{ github.ref_name }}-win-x86_64-sha256sum.txt -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: Continuous Integration 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | linux-tests: 6 | name: Linux Tests 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | job_args: [api, config, controller, impls, libwallet, .] 11 | steps: 12 | - uses: actions/checkout@v3 13 | - name: Test ${{ matrix.job_args }} 14 | working-directory: ${{ matrix.job_args }} 15 | run: cargo test --release 16 | 17 | macos-tests: 18 | name: macOS Tests 19 | runs-on: macos-latest 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v3 23 | - name: Tests 24 | run: cargo test --release --all 25 | 26 | windows-tests: 27 | name: Windows Tests 28 | runs-on: windows-latest 29 | steps: 30 | - name: Checkout 31 | uses: actions/checkout@v3 32 | - name: Tests 33 | run: cargo test --release --all -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | .DS_Store 3 | .grin* 4 | .mwc* 5 | node* 6 | !node_clients 7 | !node_clients.rs 8 | target 9 | */Cargo.lock 10 | *.iml 11 | grin.log 12 | wallet.seed 13 | test_output 14 | .idea/ 15 | -------------------------------------------------------------------------------- /.hooks/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Copyright 2018 The Grin Developers 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | rustfmt --version &>/dev/null 18 | if [ $? != 0 ]; then 19 | printf "[pre_commit] \033[0;31merror\033[0m: \"rustfmt\" not available. \n" 20 | printf "[pre_commit] \033[0;31merror\033[0m: rustfmt can be installed via - \n" 21 | printf "[pre_commit] $ rustup component add rustfmt-preview \n" 22 | exit 1 23 | fi 24 | 25 | result=0 26 | problem_files=() 27 | 28 | printf "[pre_commit] rustfmt " 29 | 30 | for file in $(git diff --name-only --cached); do 31 | if [ ${file: -3} == ".rs" ]; then 32 | # first collect all the files that need reformatting 33 | rustfmt --check $file &>/dev/null 34 | if [ $? != 0 ]; then 35 | problem_files+=($file) 36 | result=1 37 | fi 38 | fi 39 | done 40 | 41 | # now reformat all the files that need reformatting 42 | for file in ${problem_files[@]}; do 43 | rustfmt $file 44 | done 45 | 46 | # and let the user know what just happened (and which files were affected) 47 | printf "\033[0;32mok\033[0m \n" 48 | if [ $result != 0 ]; then 49 | # printf "\033[0;31mrustfmt\033[0m \n" 50 | printf "[pre_commit] the following files were rustfmt'd (not yet committed): \n" 51 | 52 | for file in ${problem_files[@]}; do 53 | printf "\033[0;31m $file\033[0m \n" 54 | done 55 | fi 56 | 57 | exit 0 58 | # to actually fail the build on rustfmt failure - 59 | # exit $result 60 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mwc_wallet" 3 | version = "5.3.6" 4 | authors = ["Mwc Developers "] 5 | description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format." 6 | license = "Apache-2.0" 7 | repository = "https://github.com/mwc-project/mwc-wallet" 8 | keywords = [ "crypto", "mwc", "mimblewimble" ] 9 | readme = "README.md" 10 | exclude = ["**/*.mwc", "**/*.mwc2"] 11 | build = "src/build/build.rs" 12 | edition = "2018" 13 | 14 | [[bin]] 15 | name = "mwc-wallet" 16 | path = "src/bin/mwc-wallet.rs" 17 | 18 | [workspace] 19 | members = ["api", "config", "controller", "impls", "libwallet", "util"] 20 | exclude = ["integration"] 21 | 22 | [dependencies] 23 | clap = { version = "2.33", features = ["yaml"] } 24 | rpassword = "4.0" 25 | thiserror = "1" 26 | prettytable-rs = "0.10" 27 | log = "0.4" 28 | linefeed = "0.6" 29 | semver = "0.10" 30 | rustyline = "6" 31 | lazy_static = "1.4" 32 | ed25519-dalek = "1.0.0-pre.4" 33 | x25519-dalek = "0.6" 34 | # Fixing issue with bitvec 35 | funty = "=1.1.0" 36 | uuid = { version = "0.8", features = ["serde", "v4"] } 37 | shlex = "1.3.0" 38 | 39 | mwc_wallet_api = { path = "./api", version = "5.3.6" } 40 | mwc_wallet_impls = { path = "./impls", version = "5.3.6" } 41 | mwc_wallet_libwallet = { path = "./libwallet", version = "5.3.6" } 42 | mwc_wallet_controller = { path = "./controller", version = "5.3.6" } 43 | mwc_wallet_config = { path = "./config", version = "5.3.6" } 44 | mwc_wallet_util = { path = "./util", version = "5.3.6" } 45 | 46 | [build-dependencies] 47 | built = { version = "0.4", features = ["git2"]} 48 | 49 | [dev-dependencies] 50 | url = "2.1" 51 | serde = "1" 52 | serde_derive = "1" 53 | serde_json = "1" 54 | remove_dir_all = "0.7" 55 | easy-jsonrpc-mwc = { git = "https://github.com/mwcproject/easy-jsonrpc-mwc", version = "0.5.5", branch = "master" } 56 | 57 | [patch.crates-io] 58 | mwc_secp256k1zkp = { git = "https://github.com/mwcproject/rust-secp256k1-zkp", tag = "0.7.16" } 59 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # MWC Wallet 3 | 4 | This is the reference implementation of MWC's wallet. It consists of 2 major pieces: 5 | 6 | * The MWC Wallet APIs, which are intended for use by MWC community wallet developers. The wallet APIs can be directly linked into other projects or invoked via a JSON-RPC interface. 7 | 8 | * A reference command-line wallet, which provides a baseline wallet for MWC and demonstrates how the wallet APIs should be called. 9 | 10 | # Usage 11 | 12 | To use the command-line wallet, we recommend using the latest release from the Releases page. There are distributions for Linux, MacOS and Windows. 13 | 14 | Full documentation outlining how to use the command line wallet can be found on [MWC's Wiki](https://github.com/mwcproject/docs/wiki/Wallet-User-Guide) 15 | 16 | # License 17 | 18 | Apache License v2.0 19 | 20 | -------------------------------------------------------------------------------- /api/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mwc_wallet_api" 3 | version = "5.3.6" 4 | authors = ["Mwc Developers "] 5 | description = "Mwc Wallet API" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/mwcproject/mwc-wallet" 8 | keywords = [ "crypto", "mwc", "mimblewimble" ] 9 | exclude = ["**/*.mwc", "**/*.mwc2"] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | log = "0.4" 14 | uuid = { version = "0.8", features = ["serde", "v4"] } 15 | serde = "1" 16 | rand = "0.6" 17 | serde_derive = "1" 18 | serde_json = "1" 19 | chrono = { version = "0.4.11", features = ["serde"] } 20 | ring = "0.16" 21 | base64 = "0.12" 22 | ed25519-dalek = "1.0.0-pre.4" 23 | easy-jsonrpc-mwc = { git = "https://github.com/mwcproject/easy-jsonrpc-mwc", version = "0.5.5", branch = "master" } 24 | lazy_static = "1.4" 25 | 26 | mwc_wallet_libwallet = { path = "../libwallet", version = "5.3.6" } 27 | mwc_wallet_config = { path = "../config", version = "5.3.6" } 28 | mwc_wallet_impls = { path = "../impls", version = "5.3.6" } 29 | mwc_wallet_util = { path = "../util", version = "5.3.6" } 30 | 31 | [dev-dependencies] 32 | serde_json = "1" 33 | tempfile = "3.1" 34 | -------------------------------------------------------------------------------- /api/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Higher level wallet functions which can be used by callers to operate 17 | //! on the wallet, as well as helpers to invoke and instantiate wallets 18 | //! and listeners 19 | 20 | #![deny(non_upper_case_globals)] 21 | #![deny(non_camel_case_types)] 22 | #![deny(non_snake_case)] 23 | #![deny(unused_mut)] 24 | 25 | use mwc_wallet_config as config; 26 | use mwc_wallet_util::mwc_core as core; 27 | use mwc_wallet_util::mwc_keychain as keychain; 28 | use mwc_wallet_util::mwc_util as util; 29 | extern crate mwc_wallet_impls as impls; 30 | extern crate mwc_wallet_libwallet as libwallet; 31 | 32 | #[macro_use] 33 | extern crate serde_derive; 34 | extern crate serde_json; 35 | 36 | #[macro_use] 37 | extern crate log; 38 | #[macro_use] 39 | extern crate lazy_static; 40 | 41 | mod foreign; 42 | mod foreign_rpc; 43 | 44 | mod owner; 45 | mod owner_rpc_v2; 46 | mod owner_rpc_v3; 47 | 48 | mod types; 49 | 50 | pub use crate::foreign::{Foreign, ForeignCheckMiddleware, ForeignCheckMiddlewareFn}; 51 | pub use crate::foreign_rpc::ForeignRpc; 52 | pub use crate::owner::Owner; 53 | pub use crate::owner_rpc_v2::OwnerRpcV2; 54 | pub use crate::owner_rpc_v3::OwnerRpcV3; 55 | 56 | pub use crate::foreign_rpc::foreign_rpc as foreign_rpc_client; 57 | pub use crate::foreign_rpc::run_doctest_foreign; 58 | pub use crate::owner_rpc_v2::run_doctest_owner; 59 | 60 | pub use types::{ 61 | ECDHPubkey, EncryptedRequest, EncryptedResponse, EncryptionErrorResponse, JsonId, PubAddress, 62 | Token, 63 | }; 64 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # Copyright 2024 The Mwc Developers 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | trigger: 16 | branches: 17 | include: 18 | - master 19 | tags: 20 | include: ['*'] 21 | 22 | pr: 23 | branches: 24 | include: ['*'] 25 | 26 | variables: 27 | RUST_BACKTRACE: '1' 28 | RUSTFLAGS: '-C debug-assertions' 29 | ghpages_user: 'mwcproject' 30 | ghpages_repo: 'mwcproject.github.io' 31 | ghpages_auth_header: '$(echo -n "${ghpages_user}:$(github_pat)" | base64);' 32 | 33 | jobs: 34 | - job: linux 35 | timeoutInMinutes: 120 36 | pool: 37 | vmImage: ubuntu-latest 38 | strategy: 39 | matrix: 40 | test: 41 | CI_JOB: test-all 42 | release: 43 | CI_JOB: release 44 | PLATFORM: linux-x64 45 | steps: 46 | - script: | 47 | sudo apt-get update -yqq 48 | sudo apt-get purge --auto-remove clang-12 clang-format-12 llvm-12 llvm-12-dev llvm-12-tools llvm-12-runtime clang-11 clang-format-11 llvm-11 llvm-11-dev llvm-11-tools llvm-11-runtime clang-10 clang-format-10 llvm-10 llvm-10-dev llvm-10-tools llvm-10-runtime clang-9 clang-format-9 llvm-9 llvm-9-dev llvm-9-tools llvm-9-runtime 49 | sudo apt install clang-8 git curl make build-essential mesa-utils libgl1-mesa-dev openssl libssl-dev -y 50 | sudo apt-get install -yqq --no-install-recommends libncursesw5-dev 51 | - template: '.ci/test.yml' 52 | - template: '.ci/release.yml' 53 | - job: macos 54 | timeoutInMinutes: 120 55 | pool: 56 | vmImage: macos-latest 57 | strategy: 58 | matrix: 59 | test: 60 | CI_JOB: test-all 61 | release: 62 | CI_JOB: release 63 | PLATFORM: macos-x64 64 | steps: 65 | - template: '.ci/test.yml' 66 | - template: '.ci/release.yml' 67 | - job: windows 68 | pool: 69 | vmImage: windows-latest 70 | strategy: 71 | matrix: 72 | test: 73 | CI_JOB: test-all 74 | release: 75 | CI_JOB: release 76 | PLATFORM: win-x64 77 | steps: 78 | - script: | 79 | choco install -y llvm 80 | choco install -y openssl 81 | displayName: Windows Install LLVM 82 | - template: '.ci/test.yml' 83 | - template: '.ci/windows-release.yml' 84 | - job: Docs 85 | timeoutInMinutes: 60 86 | pool: 87 | vmImage: ubuntu-latest 88 | strategy: 89 | matrix: 90 | release: 91 | CI_JOB: release 92 | PLATFORM: linux-x64 93 | steps: 94 | - template: '.ci/pushdocs.yml' 95 | -------------------------------------------------------------------------------- /build_static.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cargo clean 4 | export OPENSSL_LIB_DIR=/usr/local/opt/openssl@1.1/lib/; 5 | export OPENSSL_INCLUDE_DIR=/usr/local/opt/openssl@1.1/include; 6 | export OPENSSL_STATIC=yes 7 | 8 | ROARING_ARCH=x86-64-v2 9 | 10 | cargo build --release 11 | -------------------------------------------------------------------------------- /build_static32.bat: -------------------------------------------------------------------------------- 1 | cargo clean 2 | 3 | set OPENSSL_LIB_DIR=C:\Program Files (x86)\OpenSSL-Win32/lib/ 4 | set OPENSSL_INCLUDE_DIR=C:\Program Files (x86)\OpenSSL-Win32/include 5 | set OPENSSL_STATIC=yes 6 | set LIBCLANG_PATH=C:\Program Files (x86)\LLVM\bin\ 7 | 8 | call .ci\win32_cargo.bat +stable-i686-pc-windows-msvc build --release 9 | -------------------------------------------------------------------------------- /build_static64.bat: -------------------------------------------------------------------------------- 1 | cargo clean 2 | 3 | set OPENSSL_LIB_DIR=C:\Program Files\OpenSSL-Win64\lib\ 4 | set OPENSSL_INCLUDE_DIR=C:\Program Files\OpenSSL-Win64\include 5 | set OPENSSL_STATIC=yes 6 | set LIBCLANG_PATH=C:\Program Files\LLVM\bin\libclang.dll 7 | 8 | call .ci\win64_cargo.bat build --release 9 | -------------------------------------------------------------------------------- /build_static_linux.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cargo clean 4 | export OPENSSL_LIB_DIR=/usr/lib/x86_64-linux-gnu/ 5 | export OPENSSL_INCLUDE_DIR=/usr/include/openssl/ 6 | export OPENSSL_STATIC=yes 7 | 8 | ROARING_ARCH=x86-64-v2 9 | 10 | cargo build --release 11 | 12 | -------------------------------------------------------------------------------- /config/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mwc_wallet_config" 3 | version = "5.3.6" 4 | authors = ["Mwc Developers "] 5 | description = "Configuration for mwc wallet , a simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format." 6 | license = "Apache-2.0" 7 | repository = "https://github.com/mwcproject/mwc-wallet" 8 | keywords = [ "crypto", "mwc", "mimblewimble" ] 9 | workspace = ".." 10 | edition = "2018" 11 | 12 | [dependencies] 13 | rand = "0.6" 14 | serde = "1" 15 | dirs = "2.0" 16 | toml = "0.5" 17 | serde_derive = "1" 18 | thiserror = "1" 19 | 20 | mwc_wallet_util = { path = "../util", version = "5.3.6" } 21 | 22 | [dev-dependencies] 23 | pretty_assertions = "0.6" 24 | -------------------------------------------------------------------------------- /config/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Crate wrapping up the Mwc binary and configuration file 17 | 18 | #![deny(non_upper_case_globals)] 19 | #![deny(non_camel_case_types)] 20 | #![deny(non_snake_case)] 21 | #![deny(unused_mut)] 22 | #![warn(missing_docs)] 23 | 24 | #[macro_use] 25 | extern crate serde_derive; 26 | 27 | use mwc_wallet_util::mwc_core as core; 28 | use mwc_wallet_util::mwc_util as util; 29 | 30 | mod comments; 31 | pub mod config; 32 | #[allow(missing_docs)] 33 | pub mod types; 34 | 35 | pub use crate::config::{ 36 | config_file_exists, initial_setup_wallet, MWC_WALLET_DIR, WALLET_CONFIG_FILE_NAME, 37 | }; 38 | pub use crate::types::{ 39 | parse_node_address_string, ConfigError, GlobalWalletConfig, GlobalWalletConfigMembers, 40 | MQSConfig, TorConfig, WalletConfig, 41 | }; 42 | -------------------------------------------------------------------------------- /controller/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mwc_wallet_controller" 3 | version = "5.3.6" 4 | authors = ["Mwc Developers "] 5 | description = "Controllers for mwc wallet instantiation" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/mwcproject/mwc-wallet" 8 | keywords = [ "crypto", "mwc", "mimblewimble" ] 9 | exclude = ["**/*.mwc", "**/*.mwc2"] 10 | #build = "src/build/build.rs" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | futures = "0.3" 15 | hyper = "0.13" 16 | rand = "0.7" 17 | serde = "1" 18 | serde_derive = "1" 19 | serde_json = "1" 20 | log = "0.4" 21 | prettytable-rs = "0.10" 22 | ring = "0.16" 23 | term = "0.6" 24 | tokio = { version = "0.2", features = ["full"] } 25 | uuid = { version = "0.8", features = ["serde", "v4"] } 26 | url = "2.1" 27 | chrono = { version = "0.4.11", features = ["serde"] } 28 | lazy_static = "1.4" 29 | thiserror = "1" 30 | qr_code = "1.1.0" 31 | colored = "1.6" 32 | x25519-dalek = "0.6" 33 | ed25519-dalek = "1.0.0-pre.4" 34 | mwc-wagyu-ethereum = { git = "https://github.com/mwcproject/wagyu-ethereum", version = "0.6.3", branch = "master" } 35 | mwc-libp2p = { git = "https://github.com/mwcproject/rust-libp2p", version="0.35.3", branch = "master", default-features = false, features = [ "noise", "yamux", "mplex", "dns", "tcp-tokio", "ping", "gossipsub"] } 36 | #libp2p = { path = "../../rust-libp2p", default-features = false, features = [ "noise", "yamux", "mplex", "dns", "tcp-tokio", "ping", "gossipsub"] } 37 | easy-jsonrpc-mwc = { git = "https://github.com/mwcproject/easy-jsonrpc-mwc", version = "0.5.5", branch = "master" } 38 | 39 | mwc_wallet_util = { path = "../util", version = "5.3.6" } 40 | mwc_wallet_api = { path = "../api", version = "5.3.6" } 41 | mwc_wallet_impls = { path = "../impls", version = "5.3.6" } 42 | mwc_wallet_libwallet = { path = "../libwallet", version = "5.3.6" } 43 | mwc_wallet_config = { path = "../config", version = "5.3.6" } 44 | 45 | [dev-dependencies] 46 | remove_dir_all = "0.7" 47 | -------------------------------------------------------------------------------- /controller/src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Implementation specific error types 17 | use crate::api; 18 | use crate::core::core::transaction; 19 | use crate::core::libtx; 20 | use crate::impls; 21 | use crate::keychain; 22 | 23 | /// Wallet errors, mostly wrappers around underlying crypto or I/O errors. 24 | #[derive(Clone, Eq, PartialEq, Debug, thiserror::Error)] 25 | pub enum Error { 26 | /// LibTX Error 27 | #[error("LibTx Error, {0}")] 28 | LibTX(#[from] libtx::Error), 29 | 30 | /// Impls error 31 | #[error("Impls Error, {0}")] 32 | Impls(#[from] impls::Error), 33 | 34 | /// LibWallet Error 35 | #[error("LibWallet Error, {0}")] 36 | LibWallet(String), 37 | 38 | /// Swap Error 39 | #[error("Swap error, {0}")] 40 | SwapError(String), 41 | 42 | /// Keychain error 43 | #[error("Keychain error, {0}")] 44 | Keychain(#[from] keychain::Error), 45 | 46 | /// Transaction Error 47 | #[error("Transaction error, {0}")] 48 | Transaction(#[from] transaction::Error), 49 | 50 | /// Secp Error 51 | #[error("Secp error, {0}")] 52 | Secp(String), 53 | 54 | /// Filewallet error 55 | #[error("Wallet data error: {0}")] 56 | FileWallet(&'static str), 57 | 58 | /// Error when formatting json 59 | #[error("Controller IO error, {0}")] 60 | IO(String), 61 | 62 | /// Error when formatting json 63 | #[error("Serde JSON error, {0}")] 64 | Format(String), 65 | 66 | /// Error when contacting a node through its API 67 | #[error("Node API error, {0}")] 68 | Node(#[from] api::Error), 69 | 70 | /// Error originating from hyper. 71 | #[error("Hyper error, {0}")] 72 | Hyper(String), 73 | 74 | /// Error originating from hyper uri parsing. 75 | #[error("Uri parsing error")] 76 | Uri, 77 | 78 | /// Attempt to use duplicate transaction id in separate transactions 79 | #[error("Duplicate transaction ID error, {0}")] 80 | DuplicateTransactionId(String), 81 | 82 | /// Wallet seed already exists 83 | #[error("Wallet seed file exists: {0}")] 84 | WalletSeedExists(String), 85 | 86 | /// Wallet seed doesn't exist 87 | #[error("Wallet seed doesn't exist error")] 88 | WalletSeedDoesntExist, 89 | 90 | /// Enc/Decryption Error 91 | #[error("Enc/Decryption error (check password?)")] 92 | Encryption, 93 | 94 | /// BIP 39 word list 95 | #[error("BIP39 Mnemonic (word list) Error")] 96 | Mnemonic, 97 | 98 | /// Command line argument error 99 | #[error("Invalid argument: {0}")] 100 | ArgumentError(String), 101 | 102 | /// Other 103 | #[error("Generic error: {0}")] 104 | GenericError(String), 105 | 106 | /// Listener error 107 | #[error("Listener Startup Error")] 108 | ListenerError, 109 | 110 | /// Tor Configuration Error 111 | #[error("Tor Config Error: {0}")] 112 | TorConfig(String), 113 | 114 | /// Tor Process error 115 | #[error("Tor Process Error: {0}")] 116 | TorProcess(String), 117 | 118 | /// MQS Configuration Error 119 | #[error("MQS Config Error: {0}")] 120 | MQSConfig(String), 121 | 122 | ///rejecting invoice as auto invoice acceptance is turned off 123 | #[error("Rejecting invoice as auto invoice acceptance is turned off!")] 124 | DoesNotAcceptInvoices, 125 | 126 | ///when invoice amount is too big(added with mqs feature) 127 | #[error("Rejecting invoice as amount '{0}' is too big!")] 128 | InvoiceAmountTooBig(u64), 129 | 130 | /// Verify slate messages call failure 131 | #[error("Failed verifying slate messages, {0}")] 132 | VerifySlateMessagesError(String), 133 | 134 | /// Processing swap message failure 135 | #[error("Failed processing swap messages, {0}")] 136 | ProcessSwapMessageError(String), 137 | } 138 | 139 | impl From for Error { 140 | fn from(error: mwc_wallet_libwallet::Error) -> Error { 141 | Error::LibWallet(format!("{}", error)) 142 | } 143 | } 144 | 145 | impl From for Error { 146 | fn from(error: mwc_wallet_libwallet::swap::Error) -> Error { 147 | Error::SwapError(format!("{}", error)) 148 | } 149 | } 150 | -------------------------------------------------------------------------------- /controller/src/executor.rs: -------------------------------------------------------------------------------- 1 | use crate::error::Error; 2 | use futures::task::{waker_ref, ArcWake, Context, Poll}; 3 | use futures::Future; 4 | use std::pin::Pin; 5 | use std::sync::atomic::{AtomicBool, Ordering}; 6 | use std::sync::{Arc, RwLock}; 7 | use std::thread; 8 | use std::time::Duration; 9 | 10 | // Custom executor example. It is not much related to this implementation because 11 | // rust future model was changed a lot. 12 | // https://rust-lang.github.io/async-book/02_execution/04_executor.html 13 | 14 | pub struct RunHandlerInThread { 15 | // running flag is modifiable from worker thread and Poll. 16 | running: Arc, 17 | waker: Arc>>, 18 | // Need option because join require ownership transfer. That is why can't belong to 'self' dicertly 19 | // (State, Result>) - resulting from API call as required by gotham 20 | worker_thread: RwLock>>>, 21 | } 22 | 23 | // We don't want to wake up. Our runner will be pretty dammy, but it is good enough for Rest API. 24 | struct FakeWaker {} 25 | 26 | impl ArcWake for FakeWaker { 27 | fn wake_by_ref(_arc_self: &Arc) { 28 | // No wake needed 29 | } 30 | } 31 | 32 | impl RunHandlerInThread { 33 | pub fn new(handler: F) -> RunHandlerInThread 34 | where 35 | F: Send 36 | + Sync 37 | + 'static 38 | + FnOnce() -> Pin>>>, 39 | { 40 | // 'self' variables 41 | let running = Arc::new(AtomicBool::new(true)); 42 | let waker: Arc>> = Arc::new(RwLock::new(None)); 43 | 44 | // thread variables to move 45 | let thr_waker = waker.clone(); 46 | let thr_running = running.clone(); 47 | 48 | let worker_thread = thread::Builder::new() 49 | .name("RunHandlerInThread".to_string()) 50 | .spawn(move || { 51 | let mut future = handler(); 52 | 53 | let fw = Arc::new(FakeWaker {}); 54 | let waker = waker_ref(&fw); 55 | let mut context = &mut Context::from_waker(&*waker); 56 | 57 | let result: Option>; 58 | 59 | loop { 60 | match future.as_mut().poll(&mut context) { 61 | Poll::Pending => { 62 | thread::sleep(Duration::from_millis(200)); 63 | continue; 64 | } 65 | Poll::Ready(res) => { 66 | result = Some(res); 67 | break; 68 | } 69 | } 70 | } 71 | 72 | thr_running.store(false, Ordering::Relaxed); 73 | 74 | if let Some(waker) = thr_waker.write().unwrap().take() { 75 | waker.wake(); 76 | } 77 | result.unwrap() 78 | }) 79 | .unwrap(); 80 | 81 | Self { 82 | running, 83 | waker, 84 | worker_thread: RwLock::new(Some(worker_thread)), 85 | } 86 | } 87 | } 88 | 89 | impl Future for RunHandlerInThread { 90 | type Output = Result; 91 | 92 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 93 | if self.waker.read().unwrap().is_none() { 94 | // Update current task. at the first polling. 95 | // Task is needed by thread to notify future executor that job is done 96 | self.waker.write().unwrap().replace(cx.waker().clone()); 97 | } 98 | 99 | if self.running.load(Ordering::Relaxed) { 100 | // We are still running. 101 | Poll::Pending 102 | } else { 103 | // The job is done. From the thread we should be able to reprieve the results. 104 | // Because of all ownership staff we can process this once only. 105 | // Result Must be OK with Ready or Error. 106 | // In this case futures executor guarantee call it once and satisfy get tread data once limitation 107 | 108 | // JoinHandle::join required ownership transfer. That is why it can be done once. 109 | if let Some(thr_info) = self.worker_thread.write().unwrap().take() { 110 | // Gettign results from the task 111 | let result = thr_info.join().unwrap(); 112 | Poll::Ready(result) 113 | } else { 114 | // Likely double processing. See comments above. 115 | panic!("Background thread for REST API died or double processed!"); 116 | } 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /controller/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Library module for the main wallet functionalities provided by Mwc. 17 | 18 | #[macro_use] 19 | extern crate prettytable; 20 | #[macro_use] 21 | extern crate serde_derive; 22 | extern crate serde_json; 23 | 24 | #[macro_use] 25 | extern crate log; 26 | #[macro_use] 27 | extern crate lazy_static; 28 | use mwc_wallet_api as apiwallet; 29 | use mwc_wallet_config as config; 30 | use mwc_wallet_impls as impls; 31 | use mwc_wallet_libwallet as libwallet; 32 | use mwc_wallet_util::mwc_api as api; 33 | use mwc_wallet_util::mwc_core as core; 34 | use mwc_wallet_util::mwc_keychain as keychain; 35 | use mwc_wallet_util::mwc_util as util; 36 | 37 | pub mod command; 38 | pub mod controller; 39 | pub mod display; 40 | mod error; 41 | pub mod executor; 42 | 43 | pub use crate::error::Error; 44 | -------------------------------------------------------------------------------- /controller/tests/build_output.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #[macro_use] 16 | extern crate log; 17 | extern crate mwc_wallet_controller as wallet; 18 | extern crate mwc_wallet_impls as impls; 19 | extern crate mwc_wallet_util; 20 | 21 | use impls::test_framework::LocalWalletClient; 22 | use mwc_wallet_libwallet as libwallet; 23 | use mwc_wallet_util::mwc_core::core::{OutputFeatures, Transaction}; 24 | use mwc_wallet_util::mwc_keychain::{ 25 | mnemonic, BlindingFactor, ExtKeychain, ExtKeychainPath, Keychain, SwitchCommitmentType, 26 | }; 27 | use mwc_wallet_util::mwc_util::{secp, Mutex, ZeroingString}; 28 | use rand::{thread_rng, Rng}; 29 | use std::sync::atomic::Ordering; 30 | use std::sync::Arc; 31 | use std::thread; 32 | use std::time::Duration; 33 | 34 | #[macro_use] 35 | mod common; 36 | use common::{clean_output_dir, create_wallet_proxy, setup}; 37 | 38 | fn build_output_test_impl(test_dir: &str) -> Result<(), libwallet::Error> { 39 | // Generate seed so we can verify the blinding factor is derived correctly 40 | let seed: [u8; 32] = thread_rng().gen(); 41 | let keychain = ExtKeychain::from_seed(&seed, false).unwrap(); 42 | let mnemonic = mnemonic::from_entropy(&seed).unwrap(); 43 | 44 | // Create a new proxy to simulate server and wallet responses 45 | let tx_pool: Arc>> = Arc::new(Mutex::new(Vec::new())); 46 | let mut wallet_proxy = create_wallet_proxy(test_dir.into(), tx_pool.clone()); 47 | let stopper = wallet_proxy.running.clone(); 48 | 49 | create_wallet_and_add!( 50 | client1, 51 | wallet1, 52 | mask1_i, 53 | test_dir, 54 | "wallet1", 55 | Some(ZeroingString::from(mnemonic)), 56 | &mut wallet_proxy, 57 | false 58 | ); 59 | 60 | let mask1 = (&mask1_i).as_ref(); 61 | 62 | // Set the wallet proxy listener running 63 | thread::spawn(move || { 64 | if let Err(e) = wallet_proxy.run() { 65 | error!("Wallet Proxy error: {}", e); 66 | } 67 | }); 68 | 69 | let secp = secp::Secp256k1::with_caps(secp::ContextFlag::Commit); 70 | let features = OutputFeatures::Plain; 71 | let amount = 60_000_000_000; 72 | wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |sender_api, m| { 73 | let built_output = sender_api.build_output(m, features, amount)?; 74 | 75 | let key_id = built_output.key_id; 76 | assert_eq!(key_id.to_path(), ExtKeychainPath::new(3, 0, 0, 0, 0)); 77 | 78 | let blind = built_output.blind; 79 | let key = keychain.derive_key(amount, &key_id, SwitchCommitmentType::Regular)?; 80 | assert_eq!(blind, BlindingFactor::from_secret_key(key.clone())); 81 | 82 | let output = built_output.output; 83 | assert_eq!(output.features(), features); 84 | assert_eq!(output.commitment(), secp.commit(amount, key).unwrap()); 85 | output.verify_proof(&secp)?; 86 | 87 | Ok(()) 88 | }) 89 | .unwrap(); 90 | 91 | // let logging finish 92 | stopper.store(false, Ordering::Relaxed); 93 | thread::sleep(Duration::from_millis(200)); 94 | Ok(()) 95 | } 96 | 97 | #[test] 98 | fn build_output() { 99 | let test_dir = "test_output/build_output"; 100 | setup(test_dir); 101 | if let Err(e) = build_output_test_impl(test_dir) { 102 | panic!("Libwallet Error: {}", e); 103 | } 104 | clean_output_dir(test_dir); 105 | } 106 | -------------------------------------------------------------------------------- /controller/tests/updater_thread.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Test a wallet repost command 16 | #[macro_use] 17 | extern crate log; 18 | extern crate mwc_wallet_api as api; 19 | extern crate mwc_wallet_controller as wallet; 20 | extern crate mwc_wallet_impls as impls; 21 | extern crate mwc_wallet_libwallet as libwallet; 22 | 23 | // use crate::libwallet::api_impl::owner_updater::{start_updater_log_thread, StatusMessage}; 24 | // use mwc_wallet_util::mwc_core as core; 25 | 26 | use impls::test_framework::{self, LocalWalletClient}; 27 | use mwc_wallet_util::mwc_core::global; 28 | use std::ops::DerefMut; 29 | use std::sync::atomic::Ordering; 30 | use std::sync::Arc; 31 | use std::thread; 32 | use std::time::Duration; 33 | 34 | #[macro_use] 35 | mod common; 36 | use common::{clean_output_dir, create_wallet_proxy, setup, setup_global_chain_type}; 37 | use mwc_wallet_util::mwc_core::core::Transaction; 38 | use mwc_wallet_util::mwc_util::Mutex; 39 | 40 | /// updater thread test impl 41 | fn updater_thread_test_impl(test_dir: &str) -> Result<(), wallet::Error> { 42 | global::set_local_chain_type(global::ChainTypes::AutomatedTesting); 43 | // Create a new proxy to simulate server and wallet responses 44 | let tx_pool: Arc>> = Arc::new(Mutex::new(Vec::new())); 45 | let mut wallet_proxy = create_wallet_proxy(test_dir.into(), tx_pool.clone()); 46 | let chain = wallet_proxy.chain.clone(); 47 | let stopper = wallet_proxy.running.clone(); 48 | 49 | // Create a new wallet test client, and set its queues to communicate with the 50 | // proxy 51 | create_wallet_and_add!( 52 | client1, 53 | wallet1, 54 | mask1_i, 55 | test_dir, 56 | "wallet1", 57 | None, 58 | &mut wallet_proxy, 59 | false 60 | ); 61 | let mask1 = (&mask1_i).as_ref(); 62 | create_wallet_and_add!( 63 | client2, 64 | wallet2, 65 | mask2_i, 66 | test_dir, 67 | "wallet2", 68 | None, 69 | &mut wallet_proxy, 70 | false 71 | ); 72 | let mask2 = (&mask2_i).as_ref(); 73 | 74 | // Set the wallet proxy listener running 75 | thread::spawn(move || { 76 | global::set_local_chain_type(global::ChainTypes::AutomatedTesting); 77 | if let Err(e) = wallet_proxy.run() { 78 | error!("Wallet Proxy error: {}", e); 79 | } 80 | }); 81 | 82 | // add some accounts 83 | wallet::controller::owner_single_use(Some(wallet1.clone()), mask1, None, |api, m| { 84 | api.create_account_path(m, "mining")?; 85 | api.create_account_path(m, "listener")?; 86 | Ok(()) 87 | })?; 88 | 89 | // add some accounts 90 | wallet::controller::owner_single_use(Some(wallet2.clone()), mask2, None, |api, m| { 91 | api.create_account_path(m, "account1")?; 92 | api.create_account_path(m, "account2")?; 93 | Ok(()) 94 | })?; 95 | 96 | // Get some mining done 97 | { 98 | wallet_inst!(wallet1, w); 99 | w.set_parent_key_id_by_name("mining")?; 100 | } 101 | let bh = 10u64; 102 | let _ = test_framework::award_blocks_to_wallet( 103 | &chain, 104 | wallet1.clone(), 105 | mask1, 106 | bh as usize, 107 | false, 108 | tx_pool.lock().deref_mut(), 109 | ); 110 | 111 | let owner_api = api::Owner::new(wallet1, None, None); 112 | owner_api.start_updater(mask1, Duration::from_secs(5))?; 113 | 114 | // let updater thread run a bit 115 | thread::sleep(Duration::from_secs(10)); 116 | 117 | let messages = owner_api.get_updater_messages(Some(1000))?; 118 | assert!(messages.len() >= 15); // mwc has 32 lines, mwc has 25 lines. We don't want ot validate content, it will change. Just checking that it alive. 119 | 120 | owner_api.stop_updater()?; 121 | stopper.store(false, Ordering::Relaxed); 122 | thread::sleep(Duration::from_secs(2)); 123 | Ok(()) 124 | } 125 | 126 | #[test] 127 | fn updater_thread() { 128 | // The "updater" kicks off a new thread so we need to ensure the global chain_type 129 | // is set for this to work correctly. 130 | setup_global_chain_type(); 131 | 132 | let test_dir = "test_output/updater_thread"; 133 | setup(test_dir); 134 | if let Err(e) = updater_thread_test_impl(test_dir) { 135 | panic!("Libwallet Error: {}", e); 136 | } 137 | clean_output_dir(test_dir); 138 | } 139 | -------------------------------------------------------------------------------- /doc/change_outputs.md: -------------------------------------------------------------------------------- 1 | # Overview # 2 | 3 | As of mwc-wallet 3.1.7, we add support for two new parameters which can be used via CLI or Owner API: 4 | 5 | ``` 6 | -r, --minimum_confirmations_change_outputs 7 | minimum confirmations required for change outputs. This value may only be set if the -exclude_change_outputs 8 | flag is set. [default: 1] 9 | 10 | and 11 | 12 | -x, --exclude_change_outputs If this flag is set, 'minimum_confirmations' will not apply to change outputs for 13 | this request. Instead, minimum_confirmations_change_outputs will be used as the 14 | minimum_confirmations required for change_outputs. 15 | ``` 16 | 17 | Change outputs are defined as outputs that the wallet received from it's own send transaction. 18 | 19 | # Problem Statement # 20 | 21 | Some exchanges are having problems with outputs being unavailable for excessive amounts of time. This is causing 22 | delays in withdrawals and other problems. The reason the outputs are unavailable for a long amount of time is because the users 23 | are specifying a --min_conf value that is high. This is to prevent double spend attacks. For incoming deposits, 24 | this --min_conf is needed (sometimes as high as 5,000) so that double spends can be detected before deposits are credited, but 25 | it is not needed for change outputs. 26 | 27 | # Solution # 28 | 29 | In mwc-wallet 3.1.7, we address this problem by introducing two new parameters, which are described above. These parameters 30 | allow the exchange to set a different number of confirmations for change outputs (default is 1) than in bound deposits. Since 31 | change outputs are being sent by the exchange itself, there is no risk to a double spend so a much lower value may be used. 32 | 33 | # Examples # 34 | 35 | ## CLI ## 36 | 37 | This command will send 10 MWC, but only select from outputs that have at least 5,100 confirmations or change outputs that have 38 | 1 confirmation (1 is the default value for --minimum_confirmations_change_outputs). 39 | 40 | ```mwc-wallet send -x -c 5100 -m file -d tx.tx 10``` 41 | 42 | This command does the same, except sets the minimum confirmation for change outputs to 20. 43 | 44 | ```mwc-wallet send -x -r 20 -c 5100 -m file -d tx.tx 10``` 45 | 46 | ## Owner API ## 47 | 48 | These parameters can also be used in the owner API. Here is an example curl command that specifies both 49 | 'exclude_change_outputs' and 'minimum_confirmations_change_outputs'. 50 | 51 | ```curl -u mwc:api_secret --request POST --data-raw '{"jsonrpc":"2.0","method":"init_send_tx","params":{"args":{"src_acct_name":null,"amount":10000000000000, "minimum_confirmations":5100,"max_outputs":500,"num_change_outputs":1,"selection_strategy_is_use_all":false,"message":"Good Money!","target_slate_version":null,"estimate_only":null,"send_args":{"method":"http","dest":"http://localhost:3415","finalize":true,"post_tx":true,"fluff":false},"exclude_change_outputs":true, "minimum_confirmations_change_outputs": 10,"payment_proof_recipient_address":null,"ttl_blocks":1000}},"id":1}' http://127.0.0.1:3420/v2/owner``` 52 | -------------------------------------------------------------------------------- /doc/design/goals.md: -------------------------------------------------------------------------------- 1 | 2 | Mode of Interactions 3 | ==================== 4 | 5 | There's a variety of ways wallet software can be integrated with, from hardware 6 | to automated bots to the more classic desktop wallets. No single implementation 7 | can hope to accommodate all possible interactions, especially if it wants to 8 | remain user friendly (who or whatever the user may be). With that in mind, Mwc 9 | needs to provide a healthy base for a more complete wallet ecosystem to 10 | develop. 11 | 12 | We propose to achieve this by implementing, as part of the "standard" wallet: 13 | 14 | * A good set of APIs that are flexible enough for most cases. 15 | * One or two default main mode of interaction. 16 | 17 | While not being exhaustive, the different ways we can imagine wallet software 18 | working with Mwc are the following: 19 | 20 | 1. A receive-only online wallet server. This should have some well-known network 21 | address that can be reached by a client. There should be a spending key kept 22 | offline. 23 | 1. A fully offline interaction. The sender uses her wallet to dump a file that's 24 | sent to the receiver in any practical way. The receiver builds upon that file, 25 | sending it back to the sender. The sender finalizes the transaction and sends it 26 | to a Mwc node. 27 | 1. Fully online interaction through a non-trusted 3rd party. In this mode 28 | receiver and sender both connect to a web server that facilitates the 29 | interaction. Exchanges can be all be encrypted. 30 | 1. Hardware wallet. Similar to offline but the hardware wallet interacts with 31 | a computer to produce required public keys and signatures. 32 | 1. Web wallet. A 3rd party runs the required software behind the scenes and 33 | handles some of the key generation. This could be done in a custodial, 34 | non-custodial and multisig fashion. 35 | 1. Fully programmatic. Similar to the online server, but both for receiving and 36 | sending, most likely by an automated bot of some sorts. 37 | 38 | As part of the Mwc project, we will only consider the first 2 modes of 39 | interaction. We hope that other projects and businesses will tackle other modes 40 | and perhaps even create new ones we haven't considered. 41 | 42 | Design Considerations 43 | ===================== 44 | 45 | Lower-level APIs 46 | ---------------- 47 | 48 | Rust can easily be [reused by other languages](https://doc.rust-lang.org/1.2.0/book/rust-inside-other-languages.html) 49 | like Ruby, Python or node.js, using standard FFI libraries. By providing APIs 50 | to build and manipulate commitments, related bulletproofs and aggregate 51 | signatures we can kill many birds with one stone: 52 | 53 | * Make the job of wallet implementers easier. The underlying cryptographic 54 | concepts can be quite complex. 55 | * Make wallet implementations more secure. As we provide a higher level API, 56 | there is less risk in misusing lower-level constructs. 57 | * Provide some standardization in the way aggregations are done. There are 58 | sometimes multiple ways to build a commitment or aggregate signatures or proofs 59 | in a multiparty output. 60 | * Provide more eyeballs and more security to the standard library. We need to 61 | have the wallet APIs thoroughly reviewed regardless. 62 | 63 | Receive-only Online Wallet 64 | -------------------------- 65 | 66 | To be receive only we need an aggregation between a "hot" receiving key and an 67 | offline spending key. To receive, only the receiving key should be required, to 68 | spend both keys are needed. 69 | 70 | This can work by forming a multi-party output (multisig) where only the public 71 | part of the spending key is known to the receiving server. Practically a master 72 | public key that can be derived similarly to Hierarchical Deterministic wallets 73 | would provide the best security and privacy. 74 | 75 | TODO figure out what's needed for the bulletproof. Maybe pre-compute multiple 76 | of them for ranges of receiving amounts (i.e. 1-10 mwcs, 10-100 mwcs, etc). 77 | 78 | Offline Wallet 79 | -------------- 80 | 81 | This is likely the simplest to implement, with each interaction dumping its 82 | intermediate values to a file and building off each other. 83 | -------------------------------------------------------------------------------- /doc/design/wallet-arch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwcproject/mwc-wallet/a92d7ac1f6c962605da894311ea94081880d3648/doc/design/wallet-arch.png -------------------------------------------------------------------------------- /doc/design/wallet-arch.puml: -------------------------------------------------------------------------------- 1 | @startuml mwc-wallet-overview 2 | skinparam componentStyle uml2 3 | 4 | [Mwc Node] as mwc_node 5 | 6 | folder "Provided by Mwc" as services { 7 | component foreign_api [ 8 | **Foreign API** 9 | External-Facing functions 10 | - receive_tx, build coinbase 11 | ] 12 | 13 | component owner_api [ 14 | **Owner API** 15 | Functions used by wallet owner only 16 | - retrieve outputs, retrieve txs, 17 | get balances, send, etc. . . 18 | 19 | ] 20 | component libtx [ 21 | **Transaction Library (libTx)** 22 | Lower-Level transaction functions 23 | - Build transaction (via Slate), sign, 24 | build reward, fees, etc. . . 25 | ] 26 | component libwallet [ 27 | **Wallet Library (libWallet) ** 28 | - Higher level wallet functions (select coins, 29 | update wallet from node, etc) 30 | - Service Controller 31 | (instantiate libs, start listeners) 32 | ] 33 | () "Owner HTTP Listener (localhost only)" as owner_http 34 | () "Foreign HTTP Listener" as foreign_http 35 | () "Owner Single-Use" as owner_single 36 | () "Foreign Single-Use" as foreign_single 37 | } 38 | 39 | ' Trait definitions 40 | package "Traits Implemented by Wallets" as traits { 41 | database "WalletBackend" as wallet_backend 42 | database "KeyChain" as keychain 43 | component "NodeClient" as wallet_client 44 | } 45 | 46 | note left of wallet_client 47 | - Communication layer implementation 48 | - Handles underlying communication with mwc node 49 | or other wallets 50 | - HTTP implementation provided currently, (Other, 51 | more secure protocols possible.) 52 | end note 53 | 54 | note bottom of keychain 55 | - Handles all key derivation operations 56 | end note 57 | 58 | note bottom of wallet_backend 59 | - Implements underlying storage for wallet data 60 | - LMDB storage provided in default client, others 61 | possible (Flat-file, other DBs, etc) 62 | end note 63 | 64 | libtx <--> traits 65 | libwallet <--> traits 66 | 67 | note right of traits 68 | **Default Wallet simply a struct that provides** 69 | **implementations for these 3 traits** 70 | end note 71 | 72 | ' Client Side 73 | 'package "Provided as reference implementation" { 74 | [Pure JS Wallet Client Implementation] as js_client 75 | [Command Line Wallet Client] as cl_client 76 | component web_server [ 77 | V. Light Rust Web Server - Serve static files (TBD) 78 | (Provided by default - localhost only) 79 | (Serve up pure JS client) 80 | ] 81 | '} 82 | 83 | [External Wallets] as external_wallets 84 | [External Wallets] as external_wallets_2 85 | 86 | wallet_client <--> mwc_node 87 | wallet_client <--> external_wallets_2 88 | 89 | web_server <--> owner_http 90 | js_client <-- web_server 91 | cl_client <--> owner_single 92 | cl_client <--> foreign_single 93 | 94 | owner_single <--> owner_api 95 | foreign_single <--> foreign_api 96 | 97 | libwallet <--> libtx 98 | 99 | foreign_api --> libwallet 100 | owner_api --> libwallet 101 | 102 | js_client <--> owner_http 103 | owner_http <--> owner_api 104 | external_wallets <--> foreign_http 105 | foreign_http <--> foreign_api 106 | 107 | 'layout fix 108 | 'mwc_node -[hidden]- wallet_backend 109 | 110 | @enduml -------------------------------------------------------------------------------- /doc/floonet_faucet.md: -------------------------------------------------------------------------------- 1 | # MWC Floonet Faucet / MWC Testnet Faucet 2 | 3 | If you neeed some coins to test with MWC floonet, please be free to use the faucet that listen on MWC MQS address `xmgEvZ4MCCGMJnRnNXKHBbHmSGWQchNr9uZpY5J1XXnsCFS45fsU`. 4 | 5 | Faucet is running is mwc713 wallet, so we recommend to use mwc713 wallet to request the coins. Then you can transafer them to your QT wallet or mwc-wallet. 6 | 7 | You can download mwc713 from here: https://github.com/mwcproject/mwc713/releases 8 | 9 | We are assuming that you already download, installed and provision your mwc713 wallet. Here are how you can request the coins. Please note, you can request maximun 5 MWC at a time. 10 | 11 | ### How to request the coins 12 | 13 | ``` 14 | > mwc713 --floonet 15 | Using wallet configuration file at ...... 16 | 17 | Welcome to wallet713 for MWC v4.1.0 18 | 19 | Unlock your existing wallet or type 'init' to initiate a new one 20 | Use 'help' to see available commands 21 | 22 | ERROR: The wallet is locked. Please use 'unlock' first. 23 | wallet713> 24 | wallet713> unlock -p XXXXXXXXXX 25 | Your mwcmqs address: xmj6hXXXXXXXXXXXXX 26 | wallet713> 27 | wallet713> listen -s 28 | Starting mwcmqs listener... 29 | wallet713> 30 | mwcmqs listener started for [xmj6hTX7UKAXXXXXXXXXXXXXX] tid=[kbxsjQ2TAo0jjLsl8Ib_L] 31 | 32 | wallet713> invoice 1.5 --to xmgEvZ4MCCGMJnRnNXKHBbHmSGWQchNr9uZpY5J1XXnsCFS45fsU 33 | slate [c7831053-80fb-4956-8abd-f2b270afc5ff] for [1.500000000] MWCs sent to [mwcmqs://xmgEvZ4MCCGMJnRnNXKHBbHmSGWQchNr9uZpY5J1XXnsCFS45fsU] 34 | slate [c7831053-80fb-4956-8abd-f2b270afc5ff] received back from [xmgEvZ4MCCGMJnRnNXKHBbHmSGWQchNr9uZpY5J1XXnsCFS45fsU] for [1.500000000] MWCs 35 | ``` 36 | 37 | Please note, if faucet not used for a long time, it might take few minutes to wakeup and resync with a blockchain. If you invoice failed, 38 | please wait for 10 minutes and try again. If it is still offline, please ping any moderator at Discord( https://discord.gg/n5dZaty ) 'developers' channel. 39 | 40 | ### How to return the coin 41 | 42 | When you finish with your tests, please send the coins back to faucet. 43 | ``` 44 | send 3.123 --to xmgEvZ4MCCGMJnRnNXKHBbHmSGWQchNr9uZpY5J1XXnsCFS45fsU -c 1 45 | ``` -------------------------------------------------------------------------------- /doc/installing_openssl.md: -------------------------------------------------------------------------------- 1 | The latest versions of mwc-wallet require openssl 1.1 to be installed on the system. Some popular distributions of Linux 2 | (like Ubuntu 16.04) have an older version of openssl and cannot be upgraded via package manager. In order to install openssl 3 | 1.1 on those systems, the following steps are required: 4 | 5 | 1.) Ensure the system has gcc and make (on ubuntu, this can be done with the following commands: 6 | 7 | ```# apt upgrade``` 8 | 9 | ```# apt install gcc make``` 10 | 11 | 2.) Obtain the openssl 1.1 sources. They can be obtained from the official website here: https://www.openssl.org/source/ 12 | 13 | 3.) cd into the source directory and run config script: 14 | 15 | ```# ./config``` 16 | 17 | 4.) build using make 18 | 19 | ```# make``` 20 | 21 | 5.) Install using make install (note: you may need root permission for this command): 22 | 23 | ```# make install``` 24 | 25 | 6.) Add /usr/local/lib (the default library installation directory) to your LD_LIBRARY_PATH variable 26 | 27 | ```# export LD_LIBRARY_PATH=/usr/local/lib``` 28 | 29 | 7.) Execute mwc-wallet command 30 | 31 | ```# mwc-wallet --help``` 32 | 33 | Note: you may want to add this LD_LIBRARY_PATH to your login shell so that you don't have to set it each time before running 34 | mwc-wallet. 35 | -------------------------------------------------------------------------------- /doc/samples/v3_api_node/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-sample", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "@types/connect": { 8 | "version": "3.4.33", 9 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz", 10 | "integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==", 11 | "requires": { 12 | "@types/node": "*" 13 | } 14 | }, 15 | "@types/express-serve-static-core": { 16 | "version": "4.17.2", 17 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz", 18 | "integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==", 19 | "requires": { 20 | "@types/node": "*", 21 | "@types/range-parser": "*" 22 | } 23 | }, 24 | "@types/lodash": { 25 | "version": "4.14.149", 26 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz", 27 | "integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==" 28 | }, 29 | "@types/node": { 30 | "version": "12.12.27", 31 | "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.27.tgz", 32 | "integrity": "sha512-odQFl/+B9idbdS0e8IxDl2ia/LP8KZLXhV3BUeI98TrZp0uoIzQPhGd+5EtzHmT0SMOIaPd7jfz6pOHLWTtl7A==" 33 | }, 34 | "@types/range-parser": { 35 | "version": "1.2.3", 36 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", 37 | "integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==" 38 | }, 39 | "JSONStream": { 40 | "version": "1.3.5", 41 | "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.5.tgz", 42 | "integrity": "sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==", 43 | "requires": { 44 | "jsonparse": "^1.2.0", 45 | "through": ">=2.2.7 <3" 46 | } 47 | }, 48 | "commander": { 49 | "version": "2.20.3", 50 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", 51 | "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" 52 | }, 53 | "es6-promise": { 54 | "version": "4.2.8", 55 | "resolved": "https://registry.npmjs.org/es6-promise/-/es6-promise-4.2.8.tgz", 56 | "integrity": "sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w==" 57 | }, 58 | "es6-promisify": { 59 | "version": "5.0.0", 60 | "resolved": "https://registry.npmjs.org/es6-promisify/-/es6-promisify-5.0.0.tgz", 61 | "integrity": "sha1-UQnWLz5W6pZ8S2NQWu8IKRyKUgM=", 62 | "requires": { 63 | "es6-promise": "^4.0.3" 64 | } 65 | }, 66 | "eyes": { 67 | "version": "0.1.8", 68 | "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", 69 | "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" 70 | }, 71 | "jayson": { 72 | "version": "3.2.0", 73 | "resolved": "https://registry.npmjs.org/jayson/-/jayson-3.2.0.tgz", 74 | "integrity": "sha512-DZQnwA57GcStw4soSYB2VntWXFfoWvmSarlaWePDYOWhjxT72PBM4atEBomaTaS1uqk3jFC9UO9AyWjlujo3xw==", 75 | "requires": { 76 | "@types/connect": "^3.4.32", 77 | "@types/express-serve-static-core": "^4.16.9", 78 | "@types/lodash": "^4.14.139", 79 | "@types/node": "^12.7.7", 80 | "JSONStream": "^1.3.1", 81 | "commander": "^2.12.2", 82 | "es6-promisify": "^5.0.0", 83 | "eyes": "^0.1.8", 84 | "json-stringify-safe": "^5.0.1", 85 | "lodash": "^4.17.15", 86 | "uuid": "^3.2.1" 87 | } 88 | }, 89 | "json-stringify-safe": { 90 | "version": "5.0.1", 91 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 92 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" 93 | }, 94 | "jsonparse": { 95 | "version": "1.3.1", 96 | "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.3.1.tgz", 97 | "integrity": "sha1-P02uSpH6wxX3EGL4UhzCOfE2YoA=" 98 | }, 99 | "lodash": { 100 | "version": "4.17.21", 101 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", 102 | "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" 103 | }, 104 | "through": { 105 | "version": "2.3.8", 106 | "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", 107 | "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" 108 | }, 109 | "uuid": { 110 | "version": "3.4.0", 111 | "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", 112 | "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /doc/samples/v3_api_node/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "node-sample", 3 | "version": "0.0.1", 4 | "description": "Sample of connecting to the secure OwnerAPI via node", 5 | "main": "src/index.js", 6 | "scripts": { 7 | "test": "npm test" 8 | }, 9 | "author": "", 10 | "license": "ISC", 11 | "dependencies": { 12 | "jayson": "^3.2.0" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /doc/samples/v3_api_node/readme.md: -------------------------------------------------------------------------------- 1 | # Connecting to the wallet's V3 Owner API from Node 2 | 3 | This is a small sample with code that demonstrates how to initialize the Wallet V3's Secure API and call API functions through it. 4 | 5 | To run this sample: 6 | 7 | First run the Owner API: 8 | 9 | ```.sh 10 | mwc-wallet owner_api 11 | ``` 12 | 13 | This sample doesn't use the authentication specified in the wallet's `.api_secret`, so before running the owner_api please ensure api authentication is commented out in `mwc-wallet.toml`. Including the authentication token as part of the request is a function of your json-rpc client library of choice, so it's not included in the sample to make setup a bit simpler. 14 | 15 | ensure the client url in `src\index.js` is set correctly: 16 | 17 | ```.sh 18 | const client = jayson.client.http('http://localhost:3420/v3/owner'); 19 | ``` 20 | 21 | Then (assuming node.js and npm are installed on the system): 22 | 23 | ```.sh 24 | npm install 25 | node src/index.json 26 | ``` 27 | 28 | Feel free to play around with the sample, modifying it to call whatever functions you'd like to see in operation! 29 | -------------------------------------------------------------------------------- /doc/samples/v3_api_node/src/index.js: -------------------------------------------------------------------------------- 1 | /* Sample Code for connecting to the V3 Secure API via Node 2 | * 3 | * With thanks to xiaojay of Niffler Wallet: 4 | * https://github.com/mwcfans/Niffler/blob/gw3/src/shared/walletv3.js 5 | * 6 | */ 7 | 8 | const jayson = require('jayson/promise'); 9 | const crypto = require('crypto'); 10 | 11 | const client = jayson.client.http('http://localhost:3420/v3/owner'); 12 | 13 | // Demo implementation of using `aes-256-gcm` with node.js's `crypto` lib. 14 | const aes256gcm = (shared_secret) => { 15 | const ALGO = 'aes-256-gcm'; 16 | 17 | // encrypt returns base64-encoded ciphertext 18 | const encrypt = (str, nonce) => { 19 | let key = Buffer.from(shared_secret, 'hex') 20 | const cipher = crypto.createCipheriv(ALGO, key, nonce) 21 | const enc = Buffer.concat([cipher.update(str, 'utf8'), cipher.final()]) 22 | const tag = cipher.getAuthTag() 23 | return Buffer.concat([enc, tag]).toString('base64') 24 | }; 25 | 26 | // decrypt decodes base64-encoded ciphertext into a utf8-encoded string 27 | const decrypt = (enc, nonce) => { 28 | //key,nonce is all buffer type; data is base64-encoded string 29 | let key = Buffer.from(shared_secret, 'hex') 30 | const data_ = Buffer.from(enc, 'base64') 31 | const decipher = crypto.createDecipheriv(ALGO, key, nonce) 32 | const len = data_.length 33 | const tag = data_.slice(len-16, len) 34 | const text = data_.slice(0, len-16) 35 | decipher.setAuthTag(tag) 36 | const dec = decipher.update(text, 'binary', 'utf8') + decipher.final('utf8'); 37 | return dec 38 | }; 39 | 40 | return { 41 | encrypt, 42 | decrypt, 43 | }; 44 | }; 45 | 46 | class JSONRequestEncrypted { 47 | constructor(id, method, params) { 48 | this.jsonrpc = '2.0' 49 | this.method = method 50 | this.id = id 51 | this.params = params 52 | } 53 | 54 | async send(key){ 55 | const aesCipher = aes256gcm(key); 56 | const nonce = new Buffer.from(crypto.randomBytes(12)); 57 | let enc = aesCipher.encrypt(JSON.stringify(this), nonce); 58 | console.log("Encrypted: " + enc) 59 | let params = { 60 | 'nonce': nonce.toString('hex'), 61 | 'body_enc': enc, 62 | } 63 | let response = await client.request('encrypted_request_v3', params); 64 | 65 | if (response.err) { 66 | throw response.err 67 | } 68 | 69 | const nonce2 = Buffer.from(response.result.Ok.nonce, 'hex'); 70 | const data = Buffer.from(response.result.Ok.body_enc, 'base64'); 71 | 72 | let dec = aesCipher.decrypt(data, nonce2) 73 | return dec 74 | } 75 | } 76 | 77 | async function initSecure() { 78 | let ecdh = crypto.createECDH('secp256k1') 79 | ecdh.generateKeys() 80 | let publicKey = ecdh.getPublicKey('hex', 'compressed') 81 | const params = { 82 | 'ecdh_pubkey': publicKey 83 | } 84 | let response = await client.request('init_secure_api', params); 85 | if (response.err) { 86 | throw response.err 87 | } 88 | 89 | return ecdh.computeSecret(response.result.Ok, 'hex', 'hex') 90 | } 91 | 92 | function sleep(ms) { 93 | return new Promise(resolve => setTimeout(resolve, ms)); 94 | } 95 | 96 | async function main() { 97 | let shared_key = await initSecure(); 98 | 99 | let response = await new JSONRequestEncrypted(1, 'open_wallet', { 100 | "name": null, 101 | "password": "", 102 | }).send(shared_key); 103 | 104 | let token = JSON.parse(response).result.Ok; 105 | 106 | let iterations = 1; 107 | 108 | for (i=0; i /tmp/electrumx.stdout 2> /tmp/electrumx.stderr & -------------------------------------------------------------------------------- /doc/tls-setup.md: -------------------------------------------------------------------------------- 1 | # Wallet TLS setup 2 | 3 | ## What you need 4 | * A server with a static IP address (eg `3.3.3.3`) 5 | * A domain name ownership (`example.com`) 6 | * DNS configuration for this IP (`mwc1.example.com` -> `3.3.3.3`) 7 | 8 | If you don't have a static IP you may want to consider using services like DynDNS which support dynamic IP resolving, this case is not covered by this guide, but all the next steps are equally applicable. 9 | 10 | If you don't have a domain name there is a possibility to get a TLS certificate for your IP, but you have to pay for that (so perhaps it's cheaper to buy a domain name) and it's rarely supported by certificate providers. 11 | 12 | ## I have a TLS certificate already 13 | Uncomment and update the following lines in wallet config (by default `~/.mwc/mwc-wallet.toml`): 14 | 15 | ```toml 16 | tls_certificate_file = "/path/to/my/cerificate/fullchain.pem" 17 | tls_certificate_key = "/path/to/my/cerificate/privkey.pem" 18 | ``` 19 | 20 | If you have Stratum server enabled (you run a miner) make sure that wallet listener URL starts with `https` in node config (by default `~/.mwc/mwc-server.toml`): 21 | 22 | ```toml 23 | wallet_listener_url = "https://mwc1.example.com:13415" 24 | ``` 25 | 26 | Make sure your user has read access to the files (see below for how to do it). Restart wallet. If you changed your node configuration restart `mwc` too. When you (or someone else) send mwcs to this wallet the destination (`-d` option) must start with `https://`, not with `http://`. 27 | 28 | ## I don't have a TLS certificate 29 | You can get it for free from [Let's Encrypt](https://letsencrypt.org/). To simplify the process we need `certbot`. 30 | 31 | ### Install certbot 32 | Go to [Certbot home page](https://certbot.eff.org/), choose I'm using `None of the above` and your OS (eg `Ubuntu 18.04` which will be used as an example). You will be redirected to a page with instructions like [steps for Ubuntu](https://certbot.eff.org/lets-encrypt/ubuntubionic-other). Follow instructions from `Install` section. As result you should have `certbot` installed. 33 | 34 | ### Obtain certificate 35 | If you have experince with `certboot` feel free to use any type of challenge. This guide covers the simplest case of HTTP challenge. For this you need to have a web server listening on port `80`, which requires running it as root in the simplest case. We will use the server provided by certbot. **Make sure you have port 80 open** 36 | 37 | ```sh 38 | sudo certbot certonly --standalone -d mwc1.example.com 39 | ``` 40 | 41 | It will ask you some questions, as result you should see something like: 42 | 43 | ``` 44 | Congratulations! Your certificate and chain have been saved at: 45 | /etc/letsencrypt/live/mwc1.example.com/fullchain.pem 46 | Your key file has been saved at: 47 | /etc/letsencrypt/live/mwc1.example.com/privkey.pem 48 | Your cert will expire on 2019-01-16. To obtain a new or tweaked 49 | version of this certificate in the future, simply run certbot 50 | again. To non-interactively renew *all* of your certificates, run 51 | "certbot renew" 52 | ``` 53 | 54 | ### Change permissions 55 | Now you have the certificate files but only root user can read it. We run mwc as `ubuntu` user. There are different scenarios how to fix it, the simplest one is to create a group which will have access to `/etc/letsencrypt` directory and add our user to this group. 56 | 57 | ```sh 58 | sudo groupadd tls-cert 59 | sudo usermod -a -G tls-cert ubuntu 60 | chgrp -R tls-cert /etc/letsencrypt 61 | chmod -R g=rX /etc/letsencrypt 62 | sudo chmod 2755 /etc/letsencrypt 63 | ``` 64 | 65 | The last step is needed for renewal, it makes sure that all new files will have the same group ownership. 66 | 67 | ### Update wallet config 68 | Refer to `I have a TLS certificate already` because you have it now. Use the folowing values: 69 | 70 | ```toml 71 | tls_certificate_file = "/etc/letsencrypt/live/mwc1.example.com/fullchain.pem" 72 | tls_certificate_key = "/etc/letsencrypt/live/mwc1.example.com/privkey.pem" 73 | ``` 74 | 75 | -------------------------------------------------------------------------------- /doc/tor_for_exchanges_pools.md: -------------------------------------------------------------------------------- 1 | # Overview # 2 | 3 | As of mwc-wallet 3.2.2, tor is supported externally from mwc-wallet. In the previous versions, the tor support only allowed 4 | for tor to be executed internally by mwc-wallet. There were significant performance impacts to that model. This feature will 5 | allow for real production use of tor for withdrawals by both exchanges and mining pools. 6 | 7 | # Configuration # 8 | 9 | The configuration of tor is the same as in previous versions with the exception of a new parameter in the mwc-wallet.toml 10 | [tor] section, called socks_running. By default it is set to false. If you wish to use an external tor socks proxy for sending, you will 11 | need to set this parameter to true. Please note, for listening wallet will need to run it's own instance of tor. 12 | The tor section of your mwc-wallet.toml may look like this: 13 | 14 | ``` 15 | [tor] 16 | # Whether to start tor listener on listener startup (default true) 17 | use_tor_listener = true 18 | 19 | # TOR (SOCKS) proxy server address 20 | socks_proxy_addr = "127.0.0.1:59050" 21 | 22 | #Directory to output TOR configuration to when sending 23 | send_config_dir = "/Users/test/.mwc/main" 24 | socks_running = false 25 | ``` 26 | Note: use socks_running true if you already have a tor sock process setup. To get much better performance it is 27 | recommended to have sock process allways running. Otherwise wallet will run it for every send operation. 28 | 29 | This configuration tells the mwc-wallet not to instantiate a tor instance each time send is called and instead the mwc-wallet 30 | will expect that a tor instance is already running. You are expected to have a tor instance running. 31 | 32 | Also please note. If wallet is listening, it will need to run it's own instance of tor. This instance will be used for sending. 33 | 34 | To install/configure tor, you can use the following command on linux: 35 | 36 | ```# sudo apt install tor``` 37 | 38 | Or on macos: 39 | 40 | ```# brew install tor``` 41 | 42 | To configure tor, your torrc might look like this: 43 | 44 | ``` 45 | SocksPort 127.0.0.1:59050 46 | DataDirectory ./data 47 | ``` 48 | 49 | Note that the "data" directory should exist at the same level as your torrc file and the SocksPort variable should be the same 50 | value used in the mwc-wallet.toml. 51 | 52 | To start tor, run this command: 53 | 54 | ```# tor -f /path/to/torrc``` 55 | 56 | Then sends will use this existing tor instance instead of instantiating a new one each time. The performance will be much 57 | better. 58 | 59 | # Migration information # 60 | 61 | Since previous versions of mwc-wallet did not have this socks_running parameter, if you are upgrading to this version of the 62 | wallet, you will need to add this parameter to the config file or generate a new mwc-wallet.toml with the mwc-wallet init 63 | command. 64 | 65 | # Conclusion # 66 | 67 | Instantiating a new tor instance every time is too slow and this feature allows for an externally running tor instance to be 68 | used with mwc-wallet. This is a feature that hopefully all exchanges and pools will support. The latest version of the 69 | Qt wallet makes it easy for users to accept payments via tor address and it is more secure and private for exchanges and pools 70 | to enable this withdrawal method. 71 | -------------------------------------------------------------------------------- /doc/transaction/basic-transaction-wf.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | title 3 | **Current MWC Transaction Workflow** 4 | Accurate as of Oct 10, 2018 - Master branch only 5 | end title 6 | 7 | actor "Sender" as sender 8 | actor "Recipient" as recipient 9 | entity "MWC Node" as mwc_node 10 | 11 | == Round 1 == 12 | 13 | note left of sender 14 | 1: Create Transaction **UUID** (for reference and maintaining correct state) 15 | 2: Set **lock_height** for transaction kernel (current chain height) 16 | 3: Select **inputs** using desired selection strategy 17 | 4: Calculate sum **inputs** blinding factors **xI** 18 | 5: Create **change_output** 19 | 6: Select blinding factor **xC** for **change_output** 20 | 7: Create lock function **sF** that locks **inputs** and stores **change_output** in wallet 21 | and identifying wallet transaction log entry **TS** linking **inputs + outputs** 22 | (Not executed at this point) 23 | end note 24 | note left of sender 25 | 8: Calculate **tx_weight**: MAX(-1 * **num_inputs** + 4 * (**num_change_outputs** + 1), 1) 26 | (+1 covers a single output on the receiver's side) 27 | 9: Calculate **fee**: **tx_weight** * 1_000_000 nMWC 28 | 10: Calculate total blinding excess sum for all inputs and outputs **xS1** = **xC** - **xI** (private scalar) 29 | 11: Select a random nonce **kS** (private scalar) 30 | 12: Subtract random kernel offset **oS** from **xS1**. Calculate **xS** = **xS1** - **oS** 31 | 13: Multiply **xS** and **kS** by generator G to create public curve points **xSG** and **kSG** 32 | 14: Add values to **Slate** for passing to other participants: **UUID, inputs, change_outputs,** 33 | **fee, amount, lock_height, kSG, xSG, oS** 34 | end note 35 | sender -> recipient: **Slate** 36 | == Round 2 == 37 | note right of recipient 38 | 1: Check fee against number of **inputs**, **change_outputs** +1 * **receiver_output**) 39 | 2: Create **receiver_output** 40 | 3: Choose random blinding factor for **receiver_output** **xR** (private scalar) 41 | end note 42 | note right of recipient 43 | 4: Calculate message **M** = **fee | lock_height ** 44 | 5: Choose random nonce **kR** (private scalar) 45 | 6: Multiply **xR** and **kR** by generator G to create public curve points **xRG** and **kRG** 46 | 7: Compute Schnorr challenge **e** = SHA256(**kRG** + **kSG** | **xRG** + **xSG** | **M**) 47 | 8: Compute Recipient Schnorr signature **sR** = **kR** + **e** * **xR** 48 | 9: Add **sR, xRG, kRG** to **Slate** 49 | 10: Create wallet output function **rF** that stores **receiver_output** in wallet with status "Unconfirmed" 50 | and identifying transaction log entry **TR** linking **receiver_output** with transaction. 51 | end note 52 | alt All Okay 53 | recipient --> sender: Okay - **Slate** 54 | recipient -> recipient: execute wallet output function **rF** 55 | else Any Failure 56 | recipient ->x]: Abort 57 | recipient --> sender: Error 58 | [x<- sender: Abort 59 | end 60 | == Finalize Transaction == 61 | note left of sender 62 | 1: Calculate message **M** = **fee | lock_height ** 63 | 2: Compute Schnorr challenge **e** = SHA256(**kRG** + **kSG** | **xRG** + **xSG** | **M**) 64 | 3: Verify **sR** by verifying **kRG** + **e** * **xRG** = **sRG** 65 | 4: Compute Sender Schnorr signature **sS** = **kS** + **e** * **xS** 66 | 5: Calculate final signature **s** = (**kSG**+**kRG**, **sS**+**sR**) 67 | 6: Calculate public key for **s**: **xG** = **xRG** + **xSG** 68 | 7: Verify **s** against excess values in final transaction using **xG** 69 | 8: Create Transaction Kernel Containing: 70 | Excess signature **s** 71 | Public excess **xG** 72 | **fee** 73 | **lock_height** 74 | end note 75 | sender -> sender: Create final transaction **tx** from **Slate** 76 | sender -> mwc_node: Post **tx** to mempool 77 | mwc_node --> recipient: "Ok" 78 | alt All Okay 79 | recipient --> sender: "Ok" - **UUID** 80 | sender -> sender: Execute wallet lock function **sF** 81 | ...Await confirmation... 82 | recipient -> mwc_node: Confirm **receiver_output** 83 | recipient -> recipient: Change status of **receiver_output** to "Confirmed" 84 | sender -> mwc_node: Confirm **change_output** 85 | sender -> sender: Change status of **inputs** to "Spent" 86 | sender -> sender: Change status of **change_output** to "Confirmed" 87 | else Any Error 88 | recipient -> recipient: Manually remove **receiver_output** from wallet using transaction log entry **TR** 89 | recipient ->x]: Abort 90 | recipient --> sender: Error 91 | sender -> sender: Unlock **inputs** and delete **change_output** identified in transaction log entry **TS** 92 | [x<- sender: Abort 93 | end 94 | @enduml -------------------------------------------------------------------------------- /doc/transaction/mwc-transaction-wf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mwcproject/mwc-wallet/a92d7ac1f6c962605da894311ea94081880d3648/doc/transaction/mwc-transaction-wf.png -------------------------------------------------------------------------------- /file: -------------------------------------------------------------------------------- 1 | {"version_info":{"version":2,"orig_version":2,"block_header_version":1},"num_participants":2,"id":"479ac4a9-3dc6-4435-beba-113d84d18b4f","tx":{"offset":"acd6fb2b6e2151ab809a99ecb12164184d25433fb440f2d0b8360bd3a40ede8d","body":{"inputs":[{"features":"Coinbase","commit":"09798c19b3f9be463e4543bfd9da1ad6ade71aa753073f1de19787fc0c9b019237"}],"outputs":[{"features":"Plain","commit":"09d53c6211a2f86c94e94b394179d67d7be70a73aa1a9525a95a67b2e39427efa3","proof":"ba98d24a0f99cbb7d806f40237c0a8ba2a13b1a85992ac9d3a0341ac1e821d4a4d2b3d66c8f40752f0723278bd98d267425d41cfcf0785e84ac2492c785a869303f2882a64c867ce0bb7cb0e09f1b832981681a097f8ef2f396c2912baf9a6091674f917600401e4081d4012fa1a505a6235c3fa9510c64b84a7ab5c0b446b0e1af2a21fd166b322483cb3f63d0445a2c51c9b8153d310520b8cafa7ad120601ab09f301d803ee48c7d1662fe085af33c586dc6ff242cd9a4ac8dc488bc03ec5a274b4e2b4c878fd483b2b4def5398d5a933e58234baad750af8afb4bc96b80ad9169199b6c2420309757d7939ee978207c3bc2db3d6966b350f2ac18e48ff6ad488596697479599e3bb883ddcc197493f9d3b1e5744b04e619ea4b4a6e3bb8fc897b1cd089595fc3923964eb357ba369457ac70115226de10fc717893ec47ecab86fa19a4f7de373f5227501fd388c6e640be490f45e8b7d738dd87885c1e18a3c801123fa9886fc5b18cd3d6b099b507fa4a162e59b7ffb680bf6fb1f878ef7e4c3da1d011a507db3eacb015749dbd4248616e3c625d936896d9b034d419433eba1da3db22d099b82057023f94fe6fa91147ea97d93a2ac10c32b99706679ed350abd6fa3d4f6ba09c6be2c94a404b11f957c456292cbe43de9177bd0810705978edf53f2e01d0a708b96ca68380c7f3f0780a84b708ea7f82aa9ef59b36828fe913b03cc59640029772d599918242a034e4f1ac507a5b513950a9cbb489601281fb0115f3bdc6d3dc5018e456553690ee634fd911c4d2337ad6877d6d6be0d3ce13d01b0d0c4f2015014d40386f13d1f66cde6b80b8c71be9b1b0b9c75cffc3eb8da5748c0b85a56b0230352e0b8bbf710e87a1d668f4effc438b8259e9c88aeeab8c0261b595e35d8074e9bb9159fbba0b5b0087afa77206cda92ea8ddcfc08b03"}],"kernels":[{"features":"Plain","fee":"8000000","lock_height":"0","excess":"000000000000000000000000000000000000000000000000000000000000000000","excess_sig":"00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}]}},"amount":"300000000","fee":"8000000","height":"10","lock_height":"0","participant_data":[{"id":"0","public_blind_excess":"0331d3ced07fd400b15156a5235977895e6dd23d524a9942cde26b29b41815c91b","public_nonce":"02ffc246a6422bfeb4dbc5fc6f46557381dc25c69520880a44752316e679b8ec6e","part_sig":null,"message":"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefABCDEFGHIJKLMNOPQRSTUVWXYZabcdefABCDEFGHIJKLMNOPQRSTUVWXYZabcdefABCDEFGHIJKLMNOPQRSTUVWXYZabcdefABCDEFGHIJKLMNOPQRSTUVWXYZabcdefABCDEFGHIJKLMNOPQRSTUVWXYZabcdefABCDEFGHIJKLMNOPQRSTUVWXYZabcdefABCDEFGHIJKLMNOPQRSTUVWXYZabcdef","message_sig":"589675692c079f8aa7a7a7072e398ebcb167673a390d60d3d2bbcb253f2e41c0b73eaf90286110dbb9d1ed353ca0d3cd2baac696da82b4f5f02adc9cded2396a"}]} -------------------------------------------------------------------------------- /impls/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mwc_wallet_impls" 3 | version = "5.3.6" 4 | authors = ["Mwc Developers "] 5 | description = "Concrete types derived from libwallet traits" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/mwcproject/mwc-wallet" 8 | keywords = [ "crypto", "mwc", "mimblewimble" ] 9 | exclude = ["**/*.mwc", "**/*.mwc2"] 10 | edition = "2018" 11 | 12 | [dependencies] 13 | blake2-rfc = "0.2" 14 | thiserror = "1" 15 | futures = "0.3" 16 | rand = "0.6" 17 | serde = "1" 18 | serde_derive = "1" 19 | serde_json = "1" 20 | log = "0.4" 21 | ring = "0.16" 22 | tokio = { version = "0.2", features = ["full"] } 23 | uuid = { version = "0.8", features = ["serde", "v4"] } 24 | chrono = { version = "0.4.11", features = ["serde"] } 25 | lazy_static = "1.4" 26 | reqwest = { version = "0.10", features = ["rustls-tls", "socks", "blocking"] } 27 | 28 | nanoid = "0.1.3" 29 | 30 | #Socks/Tor/Bridge/Proxy 31 | byteorder = "1" 32 | ed25519-dalek = "1.0.0-pre.4" 33 | x25519-dalek = "0.6" 34 | data-encoding = "2" 35 | regex = "1.3" 36 | timer = "0.2" 37 | sysinfo = "0.29" 38 | base64 = "0.12.0" 39 | url = "2.1" 40 | 41 | mwc-libp2p = { git = "https://github.com/mwcproject/rust-libp2p", version="0.35.3", branch = "master", default-features = false, features = [ "noise", "yamux", "mplex", "dns", "tcp-tokio", "ping", "gossipsub"] } 42 | #libp2p = { path = "../../rust-libp2p", default-features = false, features = [ "noise", "yamux", "mplex", "dns", "tcp-tokio", "ping", "gossipsub"] } 43 | 44 | mwc_wallet_util = { path = "../util", version = "5.3.6" } 45 | mwc_wallet_config = { path = "../config", version = "5.3.6" } 46 | mwc_wallet_libwallet = { path = "../libwallet", version = "5.3.6" } 47 | 48 | [dev-dependencies] 49 | "remove_dir_all" = "0.7" 50 | -------------------------------------------------------------------------------- /impls/src/backends/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod lmdb; 17 | 18 | pub use self::lmdb::{wallet_db_exists, LMDBBackend}; 19 | -------------------------------------------------------------------------------- /impls/src/client_utils/json_rpc.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // Derived from https://github.com/apoelstra/rust-jsonrpc 16 | 17 | //! JSON RPC Client functionality 18 | use serde_json; 19 | 20 | /// Builds a request 21 | pub fn build_request<'a, 'b>(name: &'a str, params: &'b serde_json::Value) -> Request<'a, 'b> { 22 | Request { 23 | method: name, 24 | params: params, 25 | id: From::from(1), 26 | jsonrpc: Some("2.0"), 27 | } 28 | } 29 | 30 | #[derive(Debug, Clone, PartialEq, Serialize)] 31 | /// A JSONRPC request object 32 | pub struct Request<'a, 'b> { 33 | /// The name of the RPC call 34 | pub method: &'a str, 35 | /// Parameters to the RPC call 36 | pub params: &'b serde_json::Value, 37 | /// Identifier for this Request, which should appear in the response 38 | pub id: serde_json::Value, 39 | /// jsonrpc field, MUST be "2.0" 40 | pub jsonrpc: Option<&'a str>, 41 | } 42 | 43 | #[derive(Debug, Clone, PartialEq, Deserialize, Serialize)] 44 | /// A JSONRPC response object 45 | pub struct Response { 46 | /// A result if there is one, or null 47 | pub result: Option, 48 | /// An error if there is one, or null 49 | pub error: Option, 50 | /// Identifier for this Request, which should match that of the request 51 | pub id: serde_json::Value, 52 | /// jsonrpc field, MUST be "2.0" 53 | pub jsonrpc: Option, 54 | } 55 | 56 | impl Response { 57 | /// Extract the result from a response 58 | pub fn result(&self) -> Result { 59 | if let Some(ref e) = self.error { 60 | return Err(Error::Rpc(e.clone())); 61 | } 62 | 63 | let result = match self.result.clone() { 64 | Some(r) => { 65 | if !r["Err"].is_null() { 66 | // we get error. Let's respond as error 67 | return Err(Error::GenericError(r["Err"].to_string())); 68 | } 69 | serde_json::from_value(r["Ok"].clone()).map_err(|e| Error::Json(format!("{}", e))) 70 | } 71 | None => serde_json::from_value(serde_json::Value::Null) 72 | .map_err(|e| Error::Json(format!("{}", e))), 73 | }?; 74 | Ok(result) 75 | } 76 | 77 | /// Extract the result from a response, consuming the response 78 | pub fn into_result(self) -> Result { 79 | if let Some(e) = self.error { 80 | return Err(Error::Rpc(e)); 81 | } 82 | self.result() 83 | } 84 | 85 | /// Return the RPC error, if there was one, but do not check the result 86 | pub fn _check_error(self) -> Result<(), Error> { 87 | if let Some(e) = self.error { 88 | Err(Error::Rpc(e)) 89 | } else { 90 | Ok(()) 91 | } 92 | } 93 | 94 | /// Returns whether or not the `result` field is empty 95 | pub fn _is_none(&self) -> bool { 96 | self.result.is_none() 97 | } 98 | } 99 | 100 | /// A library error 101 | #[derive(Debug, thiserror::Error, Clone)] 102 | pub enum Error { 103 | /// Json error 104 | #[error("Unable to parse json, {0}")] 105 | Json(String), 106 | /// Error response 107 | #[error("RPC error: {0:?}")] 108 | Rpc(RpcError), 109 | /// Internal generic Error 110 | #[error("Client error: {0}")] 111 | GenericError(String), 112 | } 113 | 114 | impl From for Error { 115 | fn from(e: serde_json::Error) -> Error { 116 | Error::Json(format!("{}", e)) 117 | } 118 | } 119 | 120 | impl From for Error { 121 | fn from(e: RpcError) -> Error { 122 | Error::Rpc(e) 123 | } 124 | } 125 | 126 | #[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] 127 | /// A JSONRPC error object 128 | pub struct RpcError { 129 | /// The integer identifier of the error 130 | pub code: i32, 131 | /// A string describing the error 132 | pub message: String, 133 | /// Additional data specific to the error 134 | pub data: Option, 135 | } 136 | -------------------------------------------------------------------------------- /impls/src/client_utils/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod client; 17 | pub mod json_rpc; 18 | 19 | pub use client::{Client, Error as ClientError}; 20 | -------------------------------------------------------------------------------- /impls/src/client_utils/socksv5.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /impls/src/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Implementation specific error types 17 | use crate::core::libtx; 18 | use crate::keychain; 19 | use mwc_wallet_util::OnionV3AddressError; 20 | 21 | /// Wallet errors, mostly wrappers around underlying crypto or I/O errors. 22 | #[derive(Clone, thiserror::Error, Eq, PartialEq, Debug)] 23 | pub enum Error { 24 | /// LibTX Error 25 | #[error("LibTx Error, {0}")] 26 | LibTX(#[from] libtx::Error), 27 | 28 | /// LibWallet Error 29 | #[error("LibWallet Error, {0}")] 30 | LibWallet(String), 31 | 32 | /// Keychain error 33 | #[error("Keychain error, {0}")] 34 | Keychain(#[from] keychain::Error), 35 | 36 | /// Onion V3 Address Error 37 | #[error("Onion V3 Address Error, {0}")] 38 | OnionV3Address(#[from] OnionV3AddressError), 39 | 40 | /// Error when obfs4proxy is not in the user path if TOR brigde is enabled 41 | #[error("Unable to find obfs4proxy binary in your path; {0}")] 42 | Obfs4proxyBin(String), 43 | 44 | /// Error the bridge input is in bad format 45 | #[error("Bridge line is in bad format; {0}")] 46 | BridgeLine(String), 47 | 48 | /// Error when formatting json 49 | #[error("IO error, {0}")] 50 | IO(String), 51 | 52 | /// Secp Error 53 | #[error("Secp error, {0}")] 54 | Secp(String), 55 | 56 | /// Error when formatting json 57 | #[error("Serde JSON error, {0}")] 58 | Format(String), 59 | 60 | /// Wallet seed already exists 61 | #[error("Wallet seed file exists: {0}")] 62 | WalletSeedExists(String), 63 | 64 | /// Wallet seed doesn't exist 65 | #[error("Wallet seed doesn't exist error")] 66 | WalletSeedDoesntExist, 67 | 68 | /// Wallet seed doesn't exist 69 | #[error("Wallet doesn't exist at {0}. {1}")] 70 | WalletDoesntExist(String, String), 71 | 72 | /// Enc/Decryption Error 73 | #[error("Enc/Decryption error (check password?), {0}")] 74 | Encryption(String), 75 | 76 | /// BIP 39 word list 77 | #[error("BIP39 Mnemonic (word list) Error, {0}")] 78 | Mnemonic(String), 79 | 80 | /// Command line argument error 81 | #[error("{0}")] 82 | ArgumentError(String), 83 | 84 | /// Tor Bridge error 85 | #[error("Tor Bridge Error, {0}")] 86 | TorBridge(String), 87 | 88 | /// Tor Proxy error 89 | #[error("Tor Proxy Error, {0}")] 90 | TorProxy(String), 91 | 92 | /// Generating ED25519 Public Key 93 | #[error("Error generating ed25519 secret key: {0}")] 94 | ED25519Key(String), 95 | 96 | /// Checking for onion address 97 | #[error("Address is not an Onion v3 Address: {0}")] 98 | NotOnion(String), 99 | 100 | /// API Error 101 | #[error("Adapter Callback Error, {0}")] 102 | ClientCallback(String), 103 | 104 | /// Tor Configuration Error 105 | #[error("Tor Config Error: {0}")] 106 | TorConfig(String), 107 | 108 | /// Tor Process error 109 | #[error("Tor Process Error: {0}")] 110 | TorProcess(String), 111 | 112 | /// Error contacting wallet API 113 | #[error("Wallet Communication Error: {0}")] 114 | WalletComms(String), 115 | 116 | /// Listener is closed issue 117 | #[error("{0} listener is closed! consider using `listen` first.")] 118 | ClosedListener(String), 119 | 120 | /// MQS generic error 121 | #[error("MQS error: {0}")] 122 | MqsGenericError(String), 123 | 124 | /// Address generic error 125 | #[error("Address error: {0}")] 126 | AddressGenericError(String), 127 | 128 | /// Get MQS invalid response 129 | #[error("{0} Sender returned invalid response.")] 130 | MqsInvalidRespose(String), 131 | 132 | /// Other 133 | #[error("Generic error: {0}")] 134 | GenericError(String), 135 | 136 | #[error("unknown address!, {0}")] 137 | UnknownAddressType(String), 138 | 139 | #[error("could not parse `{0}` to a https address!")] 140 | HttpsAddressParsingError(String), 141 | 142 | #[error("Swap message error, {0}")] 143 | SwapMessageGenericError(String), 144 | 145 | #[error("Swap deal not found error, {0}")] 146 | SwapDealGenericError(String), 147 | 148 | #[error("Error in getting swap nodes info, {0}")] 149 | SwapNodesObtainError(String), 150 | 151 | #[error("proof address mismatch {0}, {1}!")] 152 | ProofAddressMismatch(String, String), 153 | } 154 | -------------------------------------------------------------------------------- /impls/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Concrete implementations of types found in libwallet, organised this 17 | //! way mostly to avoid any circular dependencies of any kind 18 | //! Functions in this crate should not use the wallet api crate directly 19 | 20 | use blake2_rfc as blake2; 21 | 22 | #[macro_use] 23 | extern crate serde_derive; 24 | #[macro_use] 25 | extern crate log; 26 | #[macro_use] 27 | extern crate serde_json; 28 | #[macro_use] 29 | extern crate lazy_static; 30 | use mwc_wallet_libwallet as libwallet; 31 | use mwc_wallet_util::mwc_api as api; 32 | use mwc_wallet_util::mwc_chain as chain; 33 | use mwc_wallet_util::mwc_core as core; 34 | pub use mwc_wallet_util::mwc_keychain as keychain; 35 | use mwc_wallet_util::mwc_p2p; 36 | use mwc_wallet_util::mwc_store as store; 37 | use mwc_wallet_util::mwc_util as util; 38 | 39 | use mwc_wallet_config as config; 40 | 41 | pub mod adapters; 42 | mod backends; 43 | mod client_utils; 44 | mod error; 45 | pub mod lifecycle; 46 | pub mod node_clients; 47 | pub mod test_framework; 48 | pub mod tor; 49 | 50 | pub use crate::adapters::libp2p_messaging; 51 | pub use crate::adapters::{ 52 | create_sender, get_mwcmqs_brocker, init_mwcmqs_access_data, Address, AddressType, CloseReason, 53 | HttpDataSender, HttpsAddress, MWCMQPublisher, MWCMQSAddress, MWCMQSubscriber, MwcMqsChannel, 54 | PathToSlateGetter, PathToSlatePutter, Publisher, SlateGetter, SlatePutter, SlateReceiver, 55 | SlateSender, Subscriber, SubscriptionHandler, SwapMessageSender, 56 | }; 57 | pub use crate::backends::{wallet_db_exists, LMDBBackend}; 58 | pub use crate::error::Error; 59 | pub use crate::lifecycle::DefaultLCProvider; 60 | pub use crate::node_clients::HTTPNodeClient; 61 | 62 | use crate::keychain::{ExtKeychain, Keychain}; 63 | 64 | use libwallet::{NodeClient, WalletInst, WalletLCProvider}; 65 | 66 | /// Main wallet instance 67 | pub struct DefaultWalletImpl<'a, C> 68 | where 69 | C: NodeClient + 'a, 70 | { 71 | lc_provider: DefaultLCProvider<'a, C, ExtKeychain>, 72 | } 73 | 74 | impl<'a, C> DefaultWalletImpl<'a, C> 75 | where 76 | C: NodeClient + 'a, 77 | { 78 | pub fn new(node_client: C) -> Result { 79 | let lc_provider = DefaultLCProvider::new(node_client); 80 | Ok(DefaultWalletImpl { 81 | lc_provider: lc_provider, 82 | }) 83 | } 84 | } 85 | 86 | impl<'a, L, C, K> WalletInst<'a, L, C, K> for DefaultWalletImpl<'a, C> 87 | where 88 | DefaultLCProvider<'a, C, ExtKeychain>: WalletLCProvider<'a, C, K>, 89 | L: WalletLCProvider<'a, C, K>, 90 | C: NodeClient + 'a, 91 | K: Keychain + 'a, 92 | { 93 | fn lc_provider( 94 | &mut self, 95 | ) -> Result<&mut (dyn WalletLCProvider<'a, C, K> + 'a), libwallet::Error> { 96 | Ok(&mut self.lc_provider) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /impls/src/lifecycle/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod default; 17 | mod seed; 18 | 19 | pub use self::default::DefaultLCProvider; 20 | pub use seed::show_recovery_phrase; 21 | pub use seed::EncryptedWalletSeed; 22 | pub use seed::WalletSeed; 23 | -------------------------------------------------------------------------------- /impls/src/node_clients/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | pub mod http; 17 | mod resp_types; 18 | 19 | pub use self::http::HTTPNodeClient; 20 | -------------------------------------------------------------------------------- /impls/src/node_clients/resp_types.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | // Derived from https://github.com/apoelstra/rust-jsonrpc 16 | 17 | //! JSON RPC Types for V2 node client 18 | 19 | #[derive(Debug, Deserialize)] 20 | pub struct GetTipResp { 21 | pub height: u64, 22 | pub last_block_pushed: String, 23 | #[allow(dead_code)] 24 | pub prev_block_to_last: String, 25 | pub total_difficulty: u64, 26 | } 27 | 28 | #[derive(Debug, Deserialize)] 29 | pub struct GetVersionResp { 30 | pub node_version: String, 31 | pub block_header_version: u16, 32 | } 33 | -------------------------------------------------------------------------------- /impls/src/tor/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | pub mod bridge; 17 | pub mod config; 18 | pub mod process; 19 | pub mod proxy; 20 | pub mod status; 21 | -------------------------------------------------------------------------------- /impls/src/tor/status.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The MWC Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Tor status for the wallet. That data can be sharable by many components. We just 16 | //! need to know how it is running. 17 | use std::sync::RwLock; 18 | 19 | lazy_static! { 20 | // Current address that is tor is listening on (also mean that listener is running) 21 | static ref TOR_ONION_ADDRESS: RwLock> = RwLock::new(None); 22 | 23 | // Flag if listener tor process is running. (we want to keep listener and sender separately) 24 | // And we want to have them single socks port to use. That is why the tor starting process args 25 | // can be adjusted 26 | 27 | static ref TOR_SENDER_RUNNING: RwLock = RwLock::new(false); 28 | } 29 | 30 | pub fn set_tor_address(address: Option) { 31 | match address { 32 | Some(address) => (*TOR_ONION_ADDRESS.write().unwrap()).replace(address), 33 | None => (*TOR_ONION_ADDRESS.write().unwrap()).take(), 34 | }; 35 | } 36 | 37 | pub fn get_tor_address() -> Option { 38 | (*TOR_ONION_ADDRESS.read().unwrap()).clone() 39 | } 40 | 41 | pub fn set_tor_sender_running(running: bool) { 42 | (*TOR_SENDER_RUNNING.write().unwrap()) = running; 43 | } 44 | 45 | pub fn get_tor_sender_running() -> bool { 46 | (*TOR_SENDER_RUNNING.read().unwrap()).clone() 47 | } 48 | -------------------------------------------------------------------------------- /integration/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mwc_integration" 3 | version = "1.1.0" 4 | authors = ["Mwc Developers "] 5 | description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format." 6 | license = "Apache-2.0" 7 | repository = "https://github.com/mimblewimble/mwc" 8 | keywords = [ "crypto", "mwc", "mimblewimble" ] 9 | workspace = ".." 10 | edition = "2018" 11 | 12 | [dependencies] 13 | futures = "0.1" 14 | http = "0.1" 15 | itertools = "0.7" 16 | rand = "0.5" 17 | serde = "1" 18 | log = "0.4" 19 | serde_derive = "1" 20 | serde_json = "1" 21 | chrono = "0.4.11" 22 | tokio = "0.1.22" 23 | blake2-rfc = "0.2" 24 | bufstream = "0.1" 25 | 26 | #mwc_apiwallet = { path = "../apiwallet", version = "1.1.0" } 27 | #mwc_libwallet = { path = "../libwallet", version = "1.1.0" } 28 | #mwc_refwallet = { path = "../refwallet", version = "1.1.0" } 29 | #mwc_wallet_config = { path = "../config", version = "1.1.0" } 30 | 31 | #mwc_core = { git = "https://github.com/mimblewimble/mwc", branch = "milestone/1.1.0" } 32 | #mwc_keychain = { git = "https://github.com/mimblewimble/mwc", branch = "milestone/1.1.0" } 33 | #mwc_chain = { git = "https://github.com/mimblewimble/mwc", branch = "milestone/1.1.0" } 34 | #mwc_util = { git = "https://github.com/mimblewimble/mwc", branch = "milestone/1.1.0" } 35 | #mwc_api = { git = "https://github.com/mimblewimble/mwc", branch = "milestone/1.1.0" } 36 | #mwc_store = { git = "https://github.com/mimblewimble/mwc", branch = "milestone/1.1.0" } 37 | #mwc_p2p = { git = "https://github.com/mimblewimble/mwc", branch = "milestone/1.1.0" } 38 | #mwc_servers = { git = "https://github.com/mimblewimble/mwc", branch = "milestone/1.1.0" } 39 | -------------------------------------------------------------------------------- /integration/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Mwc integration test crate 17 | 18 | #![deny(non_upper_case_globals)] 19 | #![deny(non_camel_case_types)] 20 | #![deny(non_snake_case)] 21 | #![deny(unused_mut)] 22 | #![warn(missing_docs)] 23 | -------------------------------------------------------------------------------- /libwallet/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mwc_wallet_libwallet" 3 | version = "5.3.6" 4 | authors = ["Mwc Developers "] 5 | description = "Simple, private and scalable cryptocurrency implementation based on the MimbleWimble chain format." 6 | license = "Apache-2.0" 7 | repository = "https://github.com/mwcproject/mwc-wallet" 8 | keywords = [ "crypto", "mwc", "mimblewimble" ] 9 | exclude = ["**/*.mwc", "**/*.mwc2"] 10 | #build = "src/build/build.rs" 11 | edition = "2018" 12 | 13 | [dependencies] 14 | blake2-rfc = "0.2" 15 | rand = "0.6" 16 | serde = "1" 17 | serde_derive = "1" 18 | serde_json = "1" 19 | log = "0.4" 20 | uuid = { version = "0.8", features = ["serde", "v4"] } 21 | chrono = { version = "0.4.11", features = ["serde"] } 22 | lazy_static = "1.4" 23 | strum = "0.18" 24 | strum_macros = "0.18" 25 | thiserror = "1" 26 | ed25519-dalek = "1.0.0-pre.4" 27 | x25519-dalek = "0.6" 28 | byteorder = "1" 29 | data-encoding = "2" 30 | ring = "0.16" 31 | regex = "1.3" 32 | sha2 = "0.9" 33 | sha3 = "0.8" 34 | digest = "0.9" 35 | hmac = "0.11" 36 | ripemd160 = "0.9" 37 | signature = "1.1.0" 38 | hex = "0.3" 39 | base64 = "0.12" 40 | colored ="1.6" 41 | serial_test = "0.4.0" 42 | native-tls = "0.2" 43 | smaz = "0.1" 44 | bitstream-io = "0.9" 45 | bs58 = "0.3" 46 | curve25519-dalek = "2.1" 47 | crypto_box = "0.5" 48 | crc = "1.8" 49 | maplit = "1.0" 50 | num-bigint = "0.2" 51 | crossbeam-utils = "0.7" 52 | 53 | 54 | tokio = { version = "0.2.1", features = ["full"] } 55 | futures-timer = "3.0.2" 56 | 57 | 58 | bitcoin_hashes = { version = "0.9.1", features = ["serde"] } 59 | secp256k1 = { version = "0.20.1", features = ["rand"]} 60 | #ether-converter = "0.1.3" 61 | bigdecimal = "0.4.2" 62 | 63 | 64 | #libp2p = { path = "../../rust-libp2p", default-features = false, features = [ "noise", "yamux", "mplex", "dns", "tcp-tokio", "ping", "gossipsub"] } 65 | mwc-libp2p = { git = "https://github.com/mwcproject/rust-libp2p", version="0.35.3", branch = "master", default-features = false, features = [ "noise", "yamux", "mplex", "dns", "tcp-tokio", "ping", "gossipsub"] } 66 | #bitcoin = { version = "0.27", path = "../../rust-bitcoin", features = ["bitcoinconsensus"] } 67 | mwc-bitcoin = { git = "https://github.com/mwcproject/rust-bitcoin", version = "0.27.1", branch = "zkp", features = ["bitcoinconsensus"] } 68 | #bch = { path = "../../rust-bch" } 69 | mwc-bch = { git = "https://github.com/mwcproject/rust-bch", version = "0.2.0", branch = "master" } 70 | #web3 = { version = "0.15.0", default-features = false, features = ["ws-tls-async-std", "signing"] } 71 | mwc-web3 = { git = "https://github.com/mwcproject/rust-web3", version = "0.15.0", branch = "master", default-features = false, features = ["ws-tls-async-std", "signing"] } 72 | mwc-wagyu-ethereum = { git = "https://github.com/mwcproject/wagyu-ethereum", version = "0.6.3", branch = "master" } 73 | mwc-wagyu-model = { git = "https://github.com/mwcproject/wagyu-model",version = "0.6.3", branch = "master" } 74 | mwc-zcash_primitives = { git = "https://github.com/mwcproject/librustzcash", version = "0.4.0", branch = "master", features = ["transparent-inputs"] } 75 | 76 | #zcash_primitives = { path = "../../librustzcash/zcash_primitives", features = ["transparent-inputs"] } 77 | 78 | 79 | mwc_wallet_util = { path = "../util", version = "5.3.6" } 80 | mwc_wallet_config = { path = "../config", version = "5.3.6" } 81 | 82 | [target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies] 83 | libc = { version = "0.2.69", default-features = false } 84 | -------------------------------------------------------------------------------- /libwallet/src/address.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The Mwc Develope; 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Functions defining wallet 'addresses', i.e. ed2559 keys based on 16 | //! a derivation path 17 | 18 | use crate::mwc_util::from_hex; 19 | use crate::mwc_util::secp::key::SecretKey; 20 | use crate::Error; 21 | 22 | use data_encoding::BASE32; 23 | use ed25519_dalek::PublicKey as DalekPublicKey; 24 | use ed25519_dalek::SecretKey as DalekSecretKey; 25 | use sha3::{Digest, Sha3_256}; 26 | 27 | /// Output ed25519 keypair given an rust_secp256k1 SecretKey 28 | pub fn ed25519_keypair(sec_key: &SecretKey) -> Result<(DalekSecretKey, DalekPublicKey), Error> { 29 | let d_skey = match DalekSecretKey::from_bytes(&sec_key.0) { 30 | Ok(k) => k, 31 | Err(e) => { 32 | return Err(Error::ED25519Key(format!( 33 | "Unable to build Dalek key, {}", 34 | e 35 | )))? 36 | } 37 | }; 38 | let d_pub_key: DalekPublicKey = (&d_skey).into(); 39 | Ok((d_skey, d_pub_key)) 40 | } 41 | 42 | /// Output ed25519 pubkey represented by string 43 | pub fn ed25519_parse_pubkey(pub_key: &str) -> Result { 44 | let bytes = from_hex(pub_key) 45 | .map_err(|e| Error::AddressDecoding(format!("Can't parse pubkey {}, {}", pub_key, e)))?; 46 | match DalekPublicKey::from_bytes(&bytes) { 47 | Ok(k) => Ok(k), 48 | Err(e) => { 49 | return Err(Error::AddressDecoding(format!( 50 | "Not a valid public key {}, {}", 51 | pub_key, e 52 | )))? 53 | } 54 | } 55 | } 56 | 57 | /// Return the ed25519 public key represented in an onion address 58 | pub fn pubkey_from_onion_v3(onion_address: &str) -> Result { 59 | let mut input = onion_address.to_uppercase(); 60 | if input.starts_with("HTTP://") || input.starts_with("HTTPS://") { 61 | input = input.replace("HTTP://", ""); 62 | input = input.replace("HTTPS://", ""); 63 | } 64 | if input.ends_with(".ONION") { 65 | input = input.replace(".ONION", ""); 66 | } 67 | let orig_address_raw = input.clone(); 68 | // for now, just check input is the right length and try and decode from base32 69 | if input.len() != 56 { 70 | return Err(Error::AddressDecoding(format!( 71 | "Input address {} is wrong length, expected 56 symbols", 72 | input 73 | )))?; 74 | } 75 | let mut address = BASE32 76 | .decode(input.as_bytes()) 77 | .map_err(|e| { 78 | Error::AddressDecoding(format!("Input address {} is not base 32, {}", input, e)) 79 | })? 80 | .to_vec(); 81 | 82 | address.truncate(32); 83 | let key = DalekPublicKey::from_bytes(&address).map_err(|e| { 84 | Error::AddressDecoding(format!( 85 | "Provided onion V3 address is invalid (parsing dalek key), {}", 86 | e 87 | )) 88 | })?; 89 | 90 | let test_v3 = onion_v3_from_pubkey(&key).map_err(|e| { 91 | Error::AddressDecoding(format!( 92 | "Provided onion V3 address is invalid (converting from pubkey), {}", 93 | e 94 | )) 95 | })?; 96 | 97 | if test_v3.to_uppercase() != orig_address_raw.to_uppercase() { 98 | return Err(Error::AddressDecoding( 99 | "Provided onion V3 address is invalid (no match)".to_string(), 100 | ))?; 101 | } 102 | Ok(key) 103 | } 104 | 105 | /// Generate an onion address from an ed25519_dalek public key 106 | pub fn onion_v3_from_pubkey(pub_key: &DalekPublicKey) -> Result { 107 | // calculate checksum 108 | let mut hasher = Sha3_256::new(); 109 | hasher.input(b".onion checksum"); 110 | hasher.input(pub_key.as_bytes()); 111 | hasher.input([0x03u8]); 112 | let checksum = hasher.result(); 113 | 114 | let mut address_bytes = pub_key.as_bytes().to_vec(); 115 | address_bytes.push(checksum[0]); 116 | address_bytes.push(checksum[1]); 117 | address_bytes.push(0x03u8); 118 | 119 | let ret = BASE32.encode(&address_bytes); 120 | Ok(ret.to_lowercase()) 121 | } 122 | 123 | #[cfg(test)] 124 | mod test { 125 | use super::*; 126 | 127 | #[test] 128 | fn onion_v3_conversion() { 129 | let onion_address = "2a6at2obto3uvkpkitqp4wxcg6u36qf534eucbskqciturczzc5suyid"; 130 | 131 | let key = pubkey_from_onion_v3(onion_address).unwrap(); 132 | println!("Key: {:?}", &key); 133 | 134 | let out_address = onion_v3_from_pubkey(&key).unwrap(); 135 | println!("Address: {:?}", &out_address); 136 | 137 | assert_eq!(onion_address, out_address); 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /libwallet/src/api_impl.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! lower-level wallet functions which build upon core::libtx to perform wallet 17 | //! operations 18 | 19 | #![deny(non_upper_case_globals)] 20 | #![deny(non_camel_case_types)] 21 | #![deny(non_snake_case)] 22 | #![deny(unused_mut)] 23 | #![warn(missing_docs)] 24 | 25 | pub mod foreign; 26 | pub mod owner; 27 | pub mod owner_eth; 28 | pub mod owner_libp2p; 29 | pub mod owner_swap; 30 | pub mod owner_updater; 31 | pub mod types; 32 | -------------------------------------------------------------------------------- /libwallet/src/api_impl/owner_eth.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The MWC Develope; 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Generic implementation of owner API eth functions 16 | 17 | use crate::mwc_keychain::Keychain; 18 | use crate::mwc_util::Mutex; 19 | use crate::types::NodeClient; 20 | use crate::{wallet_lock, WalletInst, WalletLCProvider}; 21 | use mwc_wallet_util::mwc_core::global; 22 | use std::sync::Arc; 23 | 24 | use crate::swap::ethereum::InfuraNodeClient; 25 | use crate::swap::ethereum::*; 26 | use crate::swap::trades; 27 | use crate::swap::types::Currency; 28 | use crate::swap::Error; 29 | 30 | /// Show Wallet Info 31 | pub fn info<'a, L, C, K>( 32 | wallet_inst: Arc>>>, 33 | currency: Currency, 34 | ) -> Result<(String, String, String), Error> 35 | where 36 | L: WalletLCProvider<'a, C, K>, 37 | C: NodeClient + 'a, 38 | K: Keychain + 'a, 39 | { 40 | wallet_lock!(wallet_inst, w); 41 | let ethereum_wallet = w.get_ethereum_wallet()?; 42 | 43 | let eth_infura_project_id = trades::get_eth_infura_projectid(&Currency::Ether, &None).unwrap(); 44 | let chain = if global::is_mainnet() { 45 | "mainnet".to_string() 46 | } else { 47 | "ropsten".to_string() 48 | }; 49 | let eth_node_client = InfuraNodeClient::new( 50 | eth_infura_project_id, 51 | chain, 52 | ethereum_wallet.clone(), 53 | "".to_string(), 54 | "".to_string(), 55 | )?; 56 | let height = eth_node_client.height()?; 57 | let balance = eth_node_client.balance(currency)?; 58 | 59 | Ok(( 60 | ethereum_wallet.address.clone().unwrap(), 61 | format!("{}", height), 62 | balance.0, 63 | )) 64 | } 65 | 66 | /// get eth balance 67 | pub fn get_eth_balance(ethereum_wallet: EthereumWallet) -> Result { 68 | let eth_infura_project_id = trades::get_eth_infura_projectid(&Currency::Ether, &None).unwrap(); 69 | let chain = if global::is_mainnet() { 70 | "mainnet".to_string() 71 | } else { 72 | "ropsten".to_string() 73 | }; 74 | let eth_node_client = InfuraNodeClient::new( 75 | eth_infura_project_id, 76 | chain, 77 | ethereum_wallet.clone(), 78 | "".to_string(), 79 | "".to_string(), 80 | )?; 81 | 82 | let balance = eth_node_client.balance(Currency::Ether)?; 83 | 84 | Ok(balance.1) 85 | } 86 | 87 | /// transfer ethereum coins out 88 | pub fn transfer<'a, L, C, K>( 89 | wallet_inst: Arc>>>, 90 | currency: Currency, 91 | dest: Option, 92 | amount: Option, 93 | ) -> Result<(), Error> 94 | where 95 | L: WalletLCProvider<'a, C, K>, 96 | C: NodeClient + 'a, 97 | K: Keychain + 'a, 98 | { 99 | wallet_lock!(wallet_inst, w); 100 | let ethereum_wallet = w.get_ethereum_wallet()?; 101 | 102 | let eth_infura_project_id = trades::get_eth_infura_projectid(&Currency::Ether, &None).unwrap(); 103 | let chain = if global::is_mainnet() { 104 | "mainnet".to_string() 105 | } else { 106 | "ropsten".to_string() 107 | }; 108 | let eth_node_client = InfuraNodeClient::new( 109 | eth_infura_project_id, 110 | chain, 111 | ethereum_wallet.clone(), 112 | "".to_string(), 113 | "".to_string(), 114 | )?; 115 | 116 | let to = to_eth_address(dest.unwrap())?; 117 | let amounts = to_gnorm(amount.unwrap().as_str(), "1"); 118 | let amounts_u64 = amounts.parse::(); 119 | info!( 120 | "currency: {}, to: {}, amounts: {}, amounts_u64: {}", 121 | currency, 122 | to, 123 | amounts, 124 | amounts_u64.clone().unwrap() 125 | ); 126 | 127 | let result = eth_node_client.transfer(currency, to, amounts_u64.unwrap()); 128 | match result { 129 | Ok(_tx_hash) => Ok(()), 130 | Err(e) => Err(e), 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /libwallet/src/internal.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! lower-level wallet functions which build upon core::libtx to perform wallet 17 | //! operations 18 | 19 | #![deny(non_upper_case_globals)] 20 | #![deny(non_camel_case_types)] 21 | #![deny(non_snake_case)] 22 | #![deny(unused_mut)] 23 | #![warn(missing_docs)] 24 | 25 | pub mod keys; 26 | pub mod scan; 27 | pub mod selection; 28 | pub mod tx; 29 | pub mod updater; 30 | -------------------------------------------------------------------------------- /libwallet/src/internal/keys.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Wallet key management functions 17 | use crate::error::Error; 18 | use crate::mwc_keychain::{ChildNumber, ExtKeychain, Identifier, Keychain}; 19 | use crate::mwc_util::secp::key::SecretKey; 20 | use crate::types::{AcctPathMapping, NodeClient, WalletBackend}; 21 | use std::collections::HashSet; 22 | 23 | /// Get next available key in the wallet for a given parent 24 | pub fn next_available_key<'a, T: ?Sized, C, K>( 25 | wallet: &mut T, 26 | parent_key_id: Option<&Identifier>, 27 | ) -> Result 28 | where 29 | T: WalletBackend<'a, C, K>, 30 | C: NodeClient + 'a, 31 | K: Keychain + 'a, 32 | { 33 | let child = wallet.next_child(parent_key_id, None)?; 34 | Ok(child) 35 | } 36 | 37 | /// Retrieve an existing key from a wallet 38 | pub fn retrieve_existing_key<'a, T: ?Sized, C, K>( 39 | wallet: &T, 40 | key_id: Identifier, 41 | mmr_index: Option, 42 | ) -> Result<(Identifier, u32), Error> 43 | where 44 | T: WalletBackend<'a, C, K>, 45 | C: NodeClient + 'a, 46 | K: Keychain + 'a, 47 | { 48 | let existing = wallet.get(&key_id, &mmr_index)?; 49 | let key_id = existing.key_id.clone(); 50 | let derivation = existing.n_child; 51 | Ok((key_id, derivation)) 52 | } 53 | 54 | /// Returns a list of account to BIP32 path mappings 55 | pub fn accounts<'a, T: ?Sized, C, K>(wallet: &mut T) -> Result, Error> 56 | where 57 | T: WalletBackend<'a, C, K>, 58 | C: NodeClient + 'a, 59 | K: Keychain + 'a, 60 | { 61 | Ok(wallet.acct_path_iter().collect()) 62 | } 63 | 64 | /// Renames an account path with a new label 65 | pub fn rename_acct_path<'a, T: ?Sized, C, K>( 66 | wallet: &mut T, 67 | keychain_mask: Option<&SecretKey>, 68 | accounts: Vec, 69 | old_label: &str, 70 | label: &str, 71 | ) -> Result<(), Error> 72 | where 73 | T: WalletBackend<'a, C, K>, 74 | C: NodeClient + 'a, 75 | K: Keychain + 'a, 76 | { 77 | let label = label.to_string(); 78 | if let Some(_) = wallet.acct_path_iter().find(|l| l.label == label) { 79 | return Err(Error::AccountLabelAlreadyExists(label.clone())); 80 | } 81 | 82 | let old_label = old_label.to_string(); 83 | if old_label == "default" { 84 | return Err(Error::AccountDefaultCannotBeRenamed); 85 | } 86 | 87 | let found = wallet 88 | .acct_path_iter() 89 | .find(|l| l.label == old_label) 90 | .is_some(); 91 | 92 | if found { 93 | let mut batch = wallet.batch(keychain_mask)?; 94 | batch.rename_acct_path(accounts, &old_label, &label)?; 95 | batch.commit()?; 96 | } else { 97 | return Err(Error::AccountLabelNotExists(old_label.clone())); 98 | } 99 | 100 | Ok(()) 101 | } 102 | 103 | /// Adds an new parent account path with a given label 104 | pub fn new_acct_path<'a, T: ?Sized, C, K>( 105 | wallet: &mut T, 106 | keychain_mask: Option<&SecretKey>, 107 | label: &str, 108 | ) -> Result 109 | where 110 | T: WalletBackend<'a, C, K>, 111 | C: NodeClient + 'a, 112 | K: Keychain + 'a, 113 | { 114 | let label = label.to_owned(); 115 | if wallet.acct_path_iter().any(|l| l.label == label) { 116 | return Err(Error::AccountLabelAlreadyExists(label)); 117 | } 118 | 119 | // We're always using paths at m/k/0 for parent keys for output derivations 120 | // We try to find the first available index. Maximum will not work because there we can use reserved accounts 121 | let acc_ids: HashSet = wallet 122 | .acct_path_iter() 123 | .map(|acc| u32::from(acc.path.to_path().path[0])) 124 | .collect(); 125 | let id = (1..65536) 126 | .filter(|v| !acc_ids.contains(v)) 127 | .next() 128 | .ok_or(Error::GenericError( 129 | "Unable create a new account. Too many already exist".to_string(), 130 | ))?; 131 | 132 | let template_account = wallet.acct_path_iter().next(); 133 | 134 | let return_id = { 135 | if let Some(e) = template_account { 136 | let mut p = e.path.to_path(); 137 | p.path[0] = ChildNumber::from(id); 138 | p.to_identifier() 139 | } else { 140 | ExtKeychain::derive_key_id(2, 0, 0, 0, 0) 141 | } 142 | }; 143 | 144 | let save_path = AcctPathMapping { 145 | label: label, 146 | path: return_id.clone(), 147 | }; 148 | 149 | let mut batch = wallet.batch(keychain_mask)?; 150 | batch.save_acct_path(save_path)?; 151 | batch.commit()?; 152 | Ok(return_id) 153 | } 154 | 155 | /// Adds/sets a particular account path with a given label 156 | pub fn set_acct_path<'a, T: ?Sized, C, K>( 157 | wallet: &mut T, 158 | keychain_mask: Option<&SecretKey>, 159 | label: &str, 160 | path: &Identifier, 161 | ) -> Result<(), Error> 162 | where 163 | T: WalletBackend<'a, C, K>, 164 | C: NodeClient + 'a, 165 | K: Keychain + 'a, 166 | { 167 | let label = label.to_owned(); 168 | let save_path = AcctPathMapping { 169 | label: label, 170 | path: path.clone(), 171 | }; 172 | 173 | let mut batch = wallet.batch(keychain_mask)?; 174 | batch.save_acct_path(save_path)?; 175 | batch.commit()?; 176 | Ok(()) 177 | } 178 | -------------------------------------------------------------------------------- /libwallet/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Higher level wallet functions which can be used by callers to operate 17 | //! on the wallet, as well as helpers to invoke and instantiate wallets 18 | //! and listeners 19 | 20 | #![deny(non_upper_case_globals)] 21 | #![deny(non_camel_case_types)] 22 | #![deny(non_snake_case)] 23 | #![deny(unused_mut)] 24 | #![warn(missing_docs)] 25 | 26 | use mwc_wallet_config as config; 27 | use mwc_wallet_util::mwc_api; 28 | use mwc_wallet_util::mwc_core; 29 | use mwc_wallet_util::mwc_keychain; 30 | use mwc_wallet_util::mwc_p2p; 31 | use mwc_wallet_util::mwc_util; 32 | 33 | use mwc_wallet_util as util; 34 | 35 | use blake2_rfc as blake2; 36 | 37 | #[macro_use] 38 | extern crate serde_derive; 39 | #[macro_use] 40 | extern crate log; 41 | #[macro_use] 42 | extern crate lazy_static; 43 | 44 | extern crate strum; 45 | #[macro_use] 46 | extern crate strum_macros; 47 | 48 | extern crate hex; 49 | extern crate signature; 50 | 51 | extern crate crc; 52 | 53 | pub mod address; 54 | pub mod api_impl; 55 | /// Ring prev version internals that are needed for our internal encription functionality 56 | mod error; 57 | pub mod internal; 58 | pub mod proof; 59 | mod slate; 60 | pub mod slate_versions; 61 | pub mod slatepack; 62 | /// Atomic Swap library 63 | pub mod swap; 64 | pub mod types; 65 | extern crate bitcoin as bitcoin_lib; 66 | extern crate bitcoin_hashes; 67 | extern crate mwc_zcash_primitives as zcash; 68 | 69 | pub use crate::slatepack::{SlatePurpose, Slatepack, SlatepackArmor, Slatepacker}; 70 | 71 | pub use bitcoin::Address as BitcoinAddress; 72 | 73 | pub use crate::error::Error; 74 | pub use crate::slate::{ParticipantData, ParticipantMessageData, ParticipantMessages, Slate}; 75 | pub use crate::slate_versions::{ 76 | SlateVersion, VersionedCoinbase, VersionedSlate, CURRENT_SLATE_VERSION, 77 | MWC_BLOCK_HEADER_VERSION, 78 | }; 79 | pub use api_impl::foreign; 80 | pub use api_impl::owner; 81 | pub use api_impl::owner_eth; 82 | pub use api_impl::owner_libp2p; 83 | pub use api_impl::owner_swap; 84 | pub use api_impl::owner_updater::StatusMessage; 85 | pub use api_impl::types::{ 86 | Amount, BlockFees, BuiltOutput, InitTxArgs, InitTxSendArgs, IssueInvoiceTxArgs, 87 | NodeHeightResult, OutputCommitMapping, OwnershipProof, OwnershipProofValidation, PaymentProof, 88 | PubKeySignature, ReplayMitigationConfig, RetrieveTxQueryArgs, RetrieveTxQuerySortField, 89 | RetrieveTxQuerySortOrder, SendTXArgs, SwapStartArgs, VersionInfo, 90 | }; 91 | pub use internal::scan::{scan, set_replay_config}; 92 | pub use proof::tx_proof::TxProof; 93 | pub use proof::tx_proof::{proof_ok, verify_tx_proof_wrapper}; 94 | pub use slate_versions::ser as dalek_ser; 95 | pub use types::{ 96 | AcctPathMapping, BlockIdentifier, CbData, Context, HeaderInfo, NodeClient, NodeVersionInfo, 97 | OutputData, OutputStatus, ScannedBlockInfo, StoredProofInfo, TxLogEntry, TxLogEntryType, 98 | ViewWallet, WalletBackend, WalletInfo, WalletInst, WalletLCProvider, WalletOutputBatch, 99 | }; 100 | 101 | pub use api_impl::foreign::{get_receive_account, set_receive_account}; 102 | 103 | pub use api_impl::owner_libp2p::IntegrityContext; 104 | 105 | /// Helper for taking a lock on the wallet instance 106 | #[macro_export] 107 | macro_rules! wallet_lock { 108 | ($wallet_inst: expr, $wallet: ident) => { 109 | let inst = $wallet_inst.clone(); 110 | let mut w_lock = inst.lock(); 111 | let w_provider = w_lock.lc_provider()?; 112 | let $wallet = w_provider.wallet_inst()?; 113 | }; 114 | } 115 | 116 | /// Helper for taking a lock on the wallet instance for tests. Using unwrap instead of error reporting 117 | #[macro_export] 118 | macro_rules! wallet_lock_test { 119 | ($wallet_inst: expr, $wallet: ident) => { 120 | let inst = $wallet_inst.clone(); 121 | let mut w_lock = inst.lock(); 122 | let w_provider = w_lock.lc_provider().unwrap(); 123 | let $wallet = w_provider.wallet_inst().unwrap(); 124 | }; 125 | } 126 | -------------------------------------------------------------------------------- /libwallet/src/proof/base58.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The MWC Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::error::Error; 16 | use crate::mwc_keychain::base58; 17 | use crate::mwc_util::secp::key::PublicKey; 18 | use mwc_wallet_util::mwc_util::secp::{ContextFlag, Secp256k1}; 19 | 20 | /// 21 | pub trait Base58 { 22 | ///need to add documentation 23 | fn from_base58_check(str: &str, version_bytes: Vec) -> Result; 24 | ///need to add documentation 25 | fn to_base58_check(&self, version: Vec) -> String; 26 | } 27 | 28 | /// 29 | fn to_base58_check(data: &[u8], version: Vec) -> String { 30 | let payload: Vec = version.iter().chain(data.iter()).map(|x| *x).collect(); 31 | base58::check_encode_slice(payload.as_slice()) 32 | } 33 | 34 | /// 35 | fn from_base58_check(data: &str, version_bytes: usize) -> Result<(Vec, Vec), Error> { 36 | let payload: Vec = base58::from_check(data) 37 | .map_err(|e| Error::Base58Error(format!("Unable decode base58 string {}, {}", data, e)))?; 38 | Ok(( 39 | payload[..version_bytes].to_vec(), 40 | payload[version_bytes..].to_vec(), 41 | )) 42 | } 43 | 44 | /// 45 | pub fn serialize_public_key(secp: &Secp256k1, public_key: &PublicKey) -> Vec { 46 | let ser = public_key.serialize_vec(secp, true); 47 | ser[..].to_vec() 48 | } 49 | 50 | impl Base58 for PublicKey { 51 | fn from_base58_check(str: &str, version_expect: Vec) -> Result { 52 | let n_version = version_expect.len(); 53 | let (version_actual, key_bytes) = from_base58_check(str, n_version)?; 54 | if version_actual != version_expect { 55 | return Err(Error::Base58Error( 56 | "Address belong to another network".to_string(), 57 | )); 58 | } 59 | let secp = Secp256k1::with_caps(ContextFlag::None); 60 | PublicKey::from_slice(&secp, &key_bytes) 61 | .map_err(|e| Error::Base58Error(format!("Unable to build key from Base58, {}", e))) 62 | } 63 | 64 | fn to_base58_check(&self, version: Vec) -> String { 65 | let secp = Secp256k1::with_caps(ContextFlag::None); 66 | to_base58_check(serialize_public_key(&secp, self).as_slice(), version) 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /libwallet/src/proof/hasher.rs: -------------------------------------------------------------------------------- 1 | use digest::generic_array::GenericArray; 2 | use hmac::{Hmac, Mac, NewMac}; 3 | use ripemd160::Ripemd160; 4 | use sha2::{Digest, Sha256, Sha512}; 5 | 6 | use crate::mwc_core::global::is_floonet; 7 | use crate::mwc_keychain::extkey_bip32::{BIP32Hasher, ChildNumber, ExtendedPrivKey}; 8 | use crate::mwc_keychain::Keychain; 9 | use crate::mwc_keychain::SwitchCommitmentType; 10 | use crate::mwc_util::secp::key::SecretKey; 11 | 12 | use crate::Error; 13 | 14 | type HmacSha512 = Hmac; 15 | 16 | #[derive(Clone, Debug)] 17 | ///BIP32MwcboxHasher 18 | pub struct BIP32MwcboxHasher { 19 | is_floonet: bool, 20 | hmac_sha512: HmacSha512, 21 | } 22 | 23 | impl BIP32MwcboxHasher { 24 | /// New empty hasher 25 | pub fn new(is_floonet: bool) -> Self { 26 | Self { 27 | is_floonet, 28 | hmac_sha512: HmacSha512::new(GenericArray::from_slice(&[0u8; 128])), 29 | } 30 | } 31 | } 32 | 33 | impl BIP32Hasher for BIP32MwcboxHasher { 34 | fn network_priv(&self) -> [u8; 4] { 35 | match self.is_floonet { 36 | true => [42, 0, 0, 42], 37 | false => [42, 1, 0, 42], 38 | } 39 | } 40 | fn network_pub(&self) -> [u8; 4] { 41 | match self.is_floonet { 42 | true => [42, 0, 1, 42], 43 | false => [42, 1, 1, 42], 44 | } 45 | } 46 | fn master_seed() -> [u8; 12] { 47 | b"Grinbox_seed".to_owned() 48 | } 49 | fn init_sha512(&mut self, seed: &[u8]) { 50 | self.hmac_sha512 = HmacSha512::new_from_slice(seed).expect("HMAC can take key of any size"); 51 | } 52 | fn append_sha512(&mut self, value: &[u8]) { 53 | self.hmac_sha512.update(value); 54 | } 55 | fn result_sha512(&mut self) -> [u8; 64] { 56 | let mut result = [0; 64]; 57 | result.copy_from_slice(self.hmac_sha512.clone().finalize().into_bytes().as_slice()); 58 | result 59 | } 60 | fn sha_256(&self, input: &[u8]) -> [u8; 32] { 61 | let mut sha2_res = [0; 32]; 62 | let mut sha2 = Sha256::new(); 63 | sha2.update(input); 64 | sha2_res.copy_from_slice(sha2.finalize().as_slice()); 65 | sha2_res 66 | } 67 | fn ripemd_160(&self, input: &[u8]) -> [u8; 20] { 68 | let mut ripemd_res = [0; 20]; 69 | let mut ripemd = Ripemd160::new(); 70 | ripemd.update(input); 71 | ripemd_res.copy_from_slice(ripemd.finalize().as_slice()); 72 | ripemd_res 73 | } 74 | } 75 | 76 | ///this derive_address_key will used in both mwc-wallet and wallet713 to derive the key. 77 | pub fn derive_address_key(keychain: &K, index: u32) -> Result { 78 | let root = keychain 79 | .derive_key(713, &K::root_key_id(), SwitchCommitmentType::Regular) 80 | .map_err(|e| Error::DeriveKeyError(format!("Derive key error, {}", e)))?; 81 | let mut hasher = BIP32MwcboxHasher::new(is_floonet()); 82 | let secp = keychain.secp(); 83 | let master = ExtendedPrivKey::new_master(secp, &mut hasher, &root.0) 84 | .map_err(|e| Error::DeriveKeyError(format!("Derive key error, {}", e)))?; 85 | let private_key = master 86 | .ckd_priv(secp, &mut hasher, ChildNumber::from_normal_idx(index)) 87 | .map_err(|e| Error::DeriveKeyError(format!("Derive key error, {}", e)))?; 88 | Ok(private_key.secret_key) 89 | } 90 | -------------------------------------------------------------------------------- /libwallet/src/proof/message.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::mwc_util as util; 17 | use crate::mwc_util::secp::key::{PublicKey, SecretKey}; 18 | use crate::mwc_util::secp::Secp256k1; 19 | use rand::{thread_rng, Rng}; 20 | 21 | use super::proofaddress; 22 | use crate::error::Error; 23 | 24 | use ring::aead; 25 | use ring::pbkdf2; 26 | use std::num::NonZeroU32; 27 | 28 | /// Encrypted message, used for Tx Proofs 29 | #[derive(Debug, Serialize, Deserialize)] 30 | pub struct EncryptedMessage { 31 | /// Destination dddress for that massage 32 | pub destination: proofaddress::ProvableAddress, 33 | /// Encrypted message (normally it is a slate) 34 | encrypted_message: String, 35 | /// salt value 36 | salt: String, 37 | /// Nonce value 38 | nonce: String, 39 | } 40 | 41 | // See comments at mwc-wallet/impls/src/seed.rs 42 | // Seed is encrypted exactly the same way ... 43 | 44 | impl EncryptedMessage { 45 | /// Construct new instance 46 | pub fn new( 47 | message: String, 48 | destination: &proofaddress::ProvableAddress, 49 | receiver_public_key: &PublicKey, 50 | secret_key: &SecretKey, 51 | secp: &Secp256k1, 52 | ) -> Result { 53 | let mut common_secret = receiver_public_key.clone(); 54 | common_secret 55 | .mul_assign(&secp, secret_key) 56 | .map_err(|e| Error::TxProofGenericError(format!("Unable to encrypt message, {}", e)))?; 57 | let common_secret_ser = common_secret.serialize_vec(secp, true); 58 | let common_secret_slice = &common_secret_ser[1..33]; 59 | 60 | let salt: [u8; 8] = thread_rng().gen(); 61 | let nonce: [u8; 12] = thread_rng().gen(); 62 | let mut key = [0; 32]; 63 | pbkdf2::derive( 64 | ring::pbkdf2::PBKDF2_HMAC_SHA512, 65 | NonZeroU32::new(100).unwrap(), 66 | &salt, 67 | common_secret_slice, 68 | &mut key, 69 | ); 70 | let mut enc_bytes = message.as_bytes().to_vec(); 71 | let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, &key) 72 | .map_err(|e| Error::TxProofGenericError(format!("Unable to build a key, {}", e)))?; 73 | let sealing_key: aead::LessSafeKey = aead::LessSafeKey::new(unbound_key); 74 | let aad = aead::Aad::from(&[]); 75 | sealing_key 76 | .seal_in_place_append_tag( 77 | aead::Nonce::assume_unique_for_key(nonce), 78 | aad, 79 | &mut enc_bytes, 80 | ) 81 | .map_err(|e| Error::TxProofGenericError(format!("Unable to encrypt, {}", e)))?; 82 | 83 | Ok(EncryptedMessage { 84 | destination: destination.clone(), 85 | encrypted_message: util::to_hex(&enc_bytes), 86 | salt: util::to_hex(&salt), 87 | nonce: util::to_hex(&nonce), 88 | }) 89 | } 90 | 91 | /// Build a key that suppose to match that message 92 | pub fn key( 93 | &self, 94 | sender_public_key: &PublicKey, 95 | secret_key: &SecretKey, 96 | secp: &Secp256k1, 97 | ) -> Result<[u8; 32], Error> { 98 | let salt = util::from_hex(&self.salt).map_err(|e| { 99 | Error::TxProofGenericError(format!( 100 | "Unable to decode salt from HEX {}, {}", 101 | self.salt, e 102 | )) 103 | })?; 104 | 105 | let mut common_secret = sender_public_key.clone(); 106 | common_secret 107 | .mul_assign(&secp, secret_key) 108 | .map_err(|e| Error::TxProofGenericError(format!("Key manipulation error, {}", e)))?; 109 | let common_secret_ser = common_secret.serialize_vec(secp, true); 110 | let common_secret_slice = &common_secret_ser[1..33]; 111 | 112 | let mut key = [0; 32]; 113 | pbkdf2::derive( 114 | ring::pbkdf2::PBKDF2_HMAC_SHA512, 115 | NonZeroU32::new(100).unwrap(), 116 | &salt, 117 | common_secret_slice, 118 | &mut key, 119 | ); 120 | Ok(key) 121 | } 122 | 123 | /// Decrypt/verify message with a key 124 | pub fn decrypt_with_key(&self, key: &[u8; 32]) -> Result { 125 | let mut encrypted_message = util::from_hex(&self.encrypted_message).map_err(|e| { 126 | Error::TxProofGenericError(format!( 127 | "Unable decode message from HEX {}, {}", 128 | self.encrypted_message, e 129 | )) 130 | })?; 131 | let nonce = util::from_hex(&self.nonce).map_err(|e| { 132 | Error::TxProofGenericError(format!( 133 | "Unable decode nonce from HEX {}, {}", 134 | self.nonce, e 135 | )) 136 | })?; 137 | let mut n = [0u8; 12]; 138 | n.copy_from_slice(&nonce[0..12]); 139 | 140 | let unbound_key = aead::UnboundKey::new(&aead::CHACHA20_POLY1305, key) 141 | .map_err(|e| Error::TxProofGenericError(format!("Unable to build a key, {}", e)))?; 142 | let opening_key: aead::LessSafeKey = aead::LessSafeKey::new(unbound_key); 143 | let aad = aead::Aad::from(&[]); 144 | let decrypted_data = opening_key 145 | .open_in_place( 146 | aead::Nonce::assume_unique_for_key(n), 147 | aad, 148 | &mut encrypted_message, 149 | ) 150 | .map_err(|e| { 151 | Error::TxProofGenericError(format!("Unable to decrypt the message, {}", e)) 152 | })?; 153 | 154 | let res_msg = String::from_utf8(decrypted_data.to_vec()).map_err(|e| { 155 | Error::TxProofGenericError(format!("Decrypted message is corrupted, {}", e)) 156 | })?; 157 | Ok(res_msg) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /libwallet/src/proof/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The MWC Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! lower-level wallet functions which build upon core::libtx to perform wallet 16 | //! operations 17 | 18 | #![deny(non_upper_case_globals)] 19 | #![deny(non_camel_case_types)] 20 | #![deny(non_snake_case)] 21 | #![deny(unused_mut)] 22 | #![warn(missing_docs)] 23 | 24 | /// Some crypto releted utils. 25 | pub mod crypto; 26 | /// Key derivation that come froom mwc713. Expected that they will be used for all transports 27 | pub mod hasher; 28 | /// Proff messages 29 | pub mod message; 30 | /// Addresses 31 | pub mod proofaddress; 32 | /// Proofs that come froom mwc713. Expected that they will be used for all transports 33 | pub mod tx_proof; 34 | 35 | /// 36 | pub mod base58; 37 | -------------------------------------------------------------------------------- /libwallet/src/slatepack/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Functions and types for handling Slatepack transactions 17 | 18 | mod armor; 19 | mod packer; 20 | mod slatepack; 21 | 22 | pub use self::armor::{generate_check, max_size, min_size, SlatepackArmor, HEADER_ENC}; 23 | pub use self::packer::Slatepacker; 24 | pub use self::slatepack::{SlatePurpose, Slatepack}; 25 | -------------------------------------------------------------------------------- /libwallet/src/swap/bitcoin/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The vault713 Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | mod api; 16 | mod client; 17 | mod electrum; 18 | mod rpc; 19 | mod types; 20 | 21 | pub use api::BtcSwapApi; 22 | pub use client::*; 23 | pub use electrum::ElectrumNodeClient; 24 | pub use types::{BtcBuyerContext, BtcData, BtcSellerContext, BtcUpdate}; 25 | -------------------------------------------------------------------------------- /libwallet/src/swap/ethereum/decimal_convert.rs: -------------------------------------------------------------------------------- 1 | extern crate bigdecimal; 2 | extern crate regex; 3 | 4 | use bigdecimal::BigDecimal; 5 | use regex::Regex; 6 | use std::collections::HashMap; 7 | use std::ops::Mul; 8 | use std::process; 9 | use std::str::FromStr; 10 | 11 | /// convert expo 12 | pub fn convert<'a>(value: &str, unit: &'a str) -> HashMap<&'a str, String> { 13 | let v = to_norm(value, unit); 14 | let mut map: HashMap<&'a str, String> = HashMap::new(); 15 | 16 | map.insert(unit, BigDecimal::from_str(&value).unwrap().to_string()); 17 | 18 | if unit != "18" { 19 | map.insert("18", s(&v, "1000000000000000000")); 20 | } 21 | if unit != "15" { 22 | map.insert("15", s(&v, "1000000000000000")); 23 | } 24 | if unit != "12" { 25 | map.insert("12", s(&v, "1000000000000")); 26 | } 27 | if unit != "9" { 28 | map.insert("9", s(&v, "1000000000")); 29 | } 30 | if unit != "8" { 31 | map.insert("8", s(&v, "100000000")); 32 | } 33 | if unit != "6" { 34 | map.insert("6", s(&v, "1000000")); 35 | } 36 | if unit != "3" { 37 | map.insert("3", s(&v, "1000")); 38 | } 39 | if unit != "1" { 40 | map.insert("1", s(&v, "1")); 41 | } 42 | if unit != "+3" { 43 | map.insert("+3", s(&v, "0.001")); 44 | } 45 | if unit != "+6" { 46 | map.insert("+6", s(&v, "0.000001")); 47 | } 48 | if unit != "+9" { 49 | map.insert("+9", s(&v, "0.000000001")); 50 | } 51 | if unit != "+12" { 52 | map.insert("+12", s(&v, "0.000000000001")); 53 | } 54 | 55 | return map; 56 | } 57 | 58 | /// conver to 1 59 | pub fn to_norm(value: &str, unit: &str) -> BigDecimal { 60 | let v = BigDecimal::from_str(&value).unwrap(); 61 | 62 | if unit == "18" { 63 | return m(&v, "0.000000000000000001"); 64 | } 65 | if unit == "15" { 66 | return m(&v, "0.000000000000001"); 67 | } 68 | if unit == "12" { 69 | return m(&v, "0.000000000001"); 70 | } 71 | if unit == "9" { 72 | return m(&v, "0.000000001"); 73 | } 74 | if unit == "8" { 75 | return m(&v, "0.00000001"); 76 | } 77 | if unit == "6" { 78 | return m(&v, "0.000001"); 79 | } 80 | if unit == "3" { 81 | return m(&v, "0.001"); 82 | } 83 | if unit == "1" { 84 | return m(&v, "1"); 85 | } 86 | if unit == "+3" { 87 | return m(&v, "1000"); 88 | } 89 | if unit == "+6" { 90 | return m(&v, "1000000"); 91 | } 92 | if unit == "+9" { 93 | return m(&v, "1000000000"); 94 | } 95 | if unit == "+12" { 96 | return m(&v, "1000000000000"); 97 | } 98 | 99 | println!("unit not supported"); 100 | process::exit(1); 101 | } 102 | 103 | /// convert to 9 104 | pub fn to_gnorm(value: &str, unit: &str) -> String { 105 | return convert(&value, &unit).get("9").unwrap().to_string(); 106 | } 107 | 108 | fn m(v: &BigDecimal, u: &str) -> BigDecimal { 109 | return v.mul(&BigDecimal::from_str(u).unwrap()); 110 | } 111 | 112 | fn s(v: &BigDecimal, u: &str) -> String { 113 | return t(v.mul(&BigDecimal::from_str(u).unwrap()).to_string()); 114 | } 115 | 116 | // normalize decimal places 117 | // TODO: better way 118 | fn t(v: String) -> String { 119 | let re = Regex::new(r"(.*)\.0+$").unwrap(); 120 | let v = re.replace_all(&v, "$1").to_string(); 121 | let re = Regex::new(r"(.*\.\d+[1-9]+)(0+)$").unwrap(); 122 | return re.replace_all(&v, "$1").to_string(); 123 | } 124 | -------------------------------------------------------------------------------- /libwallet/src/swap/ethereum/erc20_swap_contract.rs: -------------------------------------------------------------------------------- 1 | /// ETH Swap contract abi 2 | pub const ERC20_SWAP_CONTRACT: &str = r#"[ 3 | { 4 | "inputs": [ 5 | { 6 | "internalType": "uint256", 7 | "name": "refundTimeInBlocks", 8 | "type": "uint256" 9 | }, 10 | { 11 | "internalType": "address", 12 | "name": "addressFromSecret", 13 | "type": "address" 14 | }, 15 | { 16 | "internalType": "address", 17 | "name": "participant", 18 | "type": "address" 19 | }, 20 | { 21 | "internalType": "address", 22 | "name": "contractAddress", 23 | "type": "address" 24 | }, 25 | { 26 | "internalType": "uint256", 27 | "name": "value", 28 | "type": "uint256" 29 | } 30 | ], 31 | "name": "initiate", 32 | "outputs": [], 33 | "stateMutability": "nonpayable", 34 | "type": "function" 35 | }, 36 | { 37 | "inputs": [ 38 | { 39 | "internalType": "address", 40 | "name": "addressFromSecret", 41 | "type": "address" 42 | }, 43 | { 44 | "internalType": "bytes32", 45 | "name": "r", 46 | "type": "bytes32" 47 | }, 48 | { 49 | "internalType": "bytes32", 50 | "name": "s", 51 | "type": "bytes32" 52 | }, 53 | { 54 | "internalType": "uint8", 55 | "name": "v", 56 | "type": "uint8" 57 | } 58 | ], 59 | "name": "redeem", 60 | "outputs": [], 61 | "stateMutability": "nonpayable", 62 | "type": "function" 63 | }, 64 | { 65 | "inputs": [ 66 | { 67 | "internalType": "address", 68 | "name": "addressFromSecret", 69 | "type": "address" 70 | } 71 | ], 72 | "name": "refund", 73 | "outputs": [], 74 | "stateMutability": "nonpayable", 75 | "type": "function" 76 | }, 77 | { 78 | "inputs": [ 79 | { 80 | "internalType": "address", 81 | "name": "addressFromSecret", 82 | "type": "address" 83 | } 84 | ], 85 | "name": "getSwapDetails", 86 | "outputs": [ 87 | { 88 | "internalType": "uint256", 89 | "name": "refundTimeInBlocks", 90 | "type": "uint256" 91 | }, 92 | { 93 | "internalType": "address", 94 | "name": "contractAddress", 95 | "type": "address" 96 | }, 97 | { 98 | "internalType": "address", 99 | "name": "initiator", 100 | "type": "address" 101 | }, 102 | { 103 | "internalType": "address", 104 | "name": "participant", 105 | "type": "address" 106 | }, 107 | { 108 | "internalType": "uint256", 109 | "name": "value", 110 | "type": "uint256" 111 | } 112 | ], 113 | "stateMutability": "view", 114 | "type": "function" 115 | } 116 | ]"#; 117 | -------------------------------------------------------------------------------- /libwallet/src/swap/ethereum/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2021 The MWC Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | pub use mwc_wagyu_ethereum::{ 16 | wordlist::*, EthereumAddress, EthereumAmount, EthereumDerivationPath, 17 | EthereumExtendedPrivateKey, EthereumExtendedPublicKey, EthereumFormat, EthereumMnemonic, 18 | EthereumNetwork, EthereumPrivateKey, EthereumPublicKey, EthereumTransaction, 19 | EthereumTransactionParameters, Goerli, Kovan, Mainnet, Rinkeby, Ropsten, 20 | }; 21 | 22 | mod api; 23 | mod client; 24 | mod decimal_convert; 25 | mod erc20_contract; 26 | mod erc20_swap_contract; 27 | mod ethereum; 28 | mod infura; 29 | mod swap_contract; 30 | mod types; 31 | 32 | pub use api::EthSwapApi; 33 | pub use client::*; 34 | pub use decimal_convert::{to_gnorm, to_norm}; 35 | pub use erc20_contract::ERC20_TOKEN_CONTRACT; 36 | pub use erc20_swap_contract::ERC20_SWAP_CONTRACT; 37 | pub use ethereum::*; 38 | pub use infura::InfuraNodeClient; 39 | pub use swap_contract::ETH_SWAP_CONTRACT; 40 | pub use types::{ 41 | eth_address, to_eth_address, to_eth_tx_hash, EthBuyerContext, EthData, EthSellerContext, 42 | EthUpdate, 43 | }; 44 | -------------------------------------------------------------------------------- /libwallet/src/swap/ethereum/swap_contract.rs: -------------------------------------------------------------------------------- 1 | /// ETH Swap contract abi 2 | pub const ETH_SWAP_CONTRACT: &str = r#"[ 3 | { 4 | "inputs": [ 5 | { 6 | "internalType": "uint256", 7 | "name": "refundTimeInBlocks", 8 | "type": "uint256" 9 | }, 10 | { 11 | "internalType": "address", 12 | "name": "addressFromSecret", 13 | "type": "address" 14 | }, 15 | { 16 | "internalType": "address", 17 | "name": "participant", 18 | "type": "address" 19 | } 20 | ], 21 | "name": "initiate", 22 | "outputs": [], 23 | "stateMutability": "payable", 24 | "type": "function", 25 | "payable": true 26 | }, 27 | { 28 | "inputs": [ 29 | { 30 | "internalType": "address", 31 | "name": "addressFromSecret", 32 | "type": "address" 33 | }, 34 | { 35 | "internalType": "bytes32", 36 | "name": "r", 37 | "type": "bytes32" 38 | }, 39 | { 40 | "internalType": "bytes32", 41 | "name": "s", 42 | "type": "bytes32" 43 | }, 44 | { 45 | "internalType": "uint8", 46 | "name": "v", 47 | "type": "uint8" 48 | } 49 | ], 50 | "name": "redeem", 51 | "outputs": [], 52 | "stateMutability": "nonpayable", 53 | "type": "function" 54 | }, 55 | { 56 | "inputs": [ 57 | { 58 | "internalType": "address", 59 | "name": "addressFromSecret", 60 | "type": "address" 61 | } 62 | ], 63 | "name": "refund", 64 | "outputs": [], 65 | "stateMutability": "nonpayable", 66 | "type": "function" 67 | }, 68 | { 69 | "inputs": [ 70 | { 71 | "internalType": "address", 72 | "name": "addressFromSecret", 73 | "type": "address" 74 | } 75 | ], 76 | "name": "getSwapDetails", 77 | "outputs": [ 78 | { 79 | "internalType": "uint256", 80 | "name": "refundTimeInBlocks", 81 | "type": "uint256" 82 | }, 83 | { 84 | "internalType": "address", 85 | "name": "initiator", 86 | "type": "address" 87 | }, 88 | { 89 | "internalType": "address", 90 | "name": "participant", 91 | "type": "address" 92 | }, 93 | { 94 | "internalType": "uint256", 95 | "name": "value", 96 | "type": "uint256" 97 | } 98 | ], 99 | "stateMutability": "view", 100 | "type": "function", 101 | "constant": true 102 | } 103 | ]"#; 104 | -------------------------------------------------------------------------------- /libwallet/src/swap/fsm/machine.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The MWC Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::swap::fsm::state::{Input, State, StateEtaInfo, StateId, StateProcessRespond}; 16 | use crate::swap::types::SwapTransactionsConfirmations; 17 | use crate::swap::{Context, Error, Swap}; 18 | use mwc_wallet_util::mwc_util::secp::Secp256k1; 19 | use std::collections::HashMap; 20 | 21 | /// Swap State machine 22 | pub struct StateMachine<'a> { 23 | /// Available States 24 | state_map: HashMap>, 25 | } 26 | 27 | impl<'a> StateMachine<'a> { 28 | /// Create 29 | pub fn new(states: Vec>) -> Self { 30 | let mut state_map: HashMap> = HashMap::new(); 31 | for st in states { 32 | let _ = state_map.insert(st.get_state_id(), st); 33 | } 34 | 35 | #[cfg(debug_assertions)] 36 | for st in state_map.values() { 37 | assert!(state_map.contains_key(&st.get_state_id())); 38 | if let Some(state) = st.get_prev_swap_state() { 39 | assert!(state_map.contains_key(&state)); 40 | } 41 | if let Some(state) = st.get_next_swap_state() { 42 | assert!(state_map.contains_key(&state)); 43 | } 44 | } 45 | 46 | StateMachine { state_map } 47 | } 48 | 49 | /// Check if this trade can be cancelled. 50 | pub fn is_cancellable(&self, swap: &Swap) -> Result { 51 | let state = self 52 | .state_map 53 | .get(&swap.state) 54 | .ok_or(Error::SwapStateMachineError(format!( 55 | "Unknown state {:?}", 56 | swap.state 57 | )))?; 58 | Ok(state.is_cancellable()) 59 | } 60 | 61 | /// Verify if the state is valid for this machine 62 | pub fn has_state(&self, state: &StateId) -> bool { 63 | self.state_map.contains_key(state) 64 | } 65 | 66 | /// Process the step 67 | pub fn process( 68 | &mut self, 69 | input: Input, 70 | swap: &mut Swap, 71 | context: &Context, 72 | height: u64, 73 | tx_conf: &SwapTransactionsConfirmations, 74 | secp: &Secp256k1, 75 | ) -> Result { 76 | debug!( 77 | "Swap {} processing state {:?} for Input {:?}", 78 | swap.id, swap.state, input 79 | ); 80 | 81 | let state = self 82 | .state_map 83 | .get_mut(&swap.state) 84 | .ok_or(Error::SwapStateMachineError(format!( 85 | "Unknown state {:?}", 86 | swap.state 87 | )))?; 88 | let mut respond = state.process(input, swap, context, height, tx_conf, secp)?; 89 | 90 | while respond.next_state_id != swap.state { 91 | debug!("New state: {:?}", swap.state); 92 | swap.state = respond.next_state_id.clone(); 93 | let state = self 94 | .state_map 95 | .get_mut(&swap.state) 96 | .ok_or(Error::SwapStateMachineError(format!( 97 | "Unknown state {:?}", 98 | swap.state 99 | )))?; 100 | respond = state.process(Input::Check, swap, context, height, tx_conf, secp)?; 101 | } 102 | respond.journal = swap.journal.clone(); 103 | 104 | debug!("Responding with {:?}", respond); 105 | Ok(respond) 106 | } 107 | 108 | /// Build a roadmap for the swap process 109 | pub fn get_swap_roadmap( 110 | &self, 111 | swap: &Swap, 112 | secp: &Secp256k1, 113 | ) -> Result, Error> { 114 | let state = self 115 | .state_map 116 | .get(&swap.state) 117 | .ok_or(Error::SwapStateMachineError(format!( 118 | "Unknown state {:?}", 119 | swap.state 120 | )))?; 121 | 122 | let mut result: Vec = Vec::new(); 123 | 124 | // go backward first 125 | let mut prev_state_id = state.get_prev_swap_state(); 126 | while prev_state_id.is_some() { 127 | let psid = prev_state_id.unwrap(); 128 | let prev_state = self 129 | .state_map 130 | .get(&psid) 131 | .ok_or(Error::SwapStateMachineError(format!( 132 | "Unknown state {:?}", 133 | psid 134 | )))?; 135 | if let Some(info) = prev_state.get_eta(swap, secp) { 136 | result.insert(0, info); 137 | } 138 | prev_state_id = prev_state.get_prev_swap_state(); 139 | } 140 | // current state 141 | if let Some(info) = state.get_eta(swap, secp) { 142 | result.push(info.active()); 143 | } 144 | // going forward 145 | let mut next_state_id = state.get_next_swap_state(); 146 | while next_state_id.is_some() { 147 | let nsid = next_state_id.unwrap(); 148 | let next_state = self 149 | .state_map 150 | .get(&nsid) 151 | .ok_or(Error::SwapStateMachineError(format!( 152 | "Unknown state {:?}", 153 | nsid 154 | )))?; 155 | if let Some(info) = next_state.get_eta(swap, secp) { 156 | result.push(info); 157 | } 158 | next_state_id = next_state.get_next_swap_state(); 159 | } 160 | 161 | Ok(result) 162 | } 163 | } 164 | -------------------------------------------------------------------------------- /libwallet/src/swap/fsm/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2020 The MWC Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | /// States parent classes and structs that describe the state machine. 16 | pub mod state; 17 | 18 | /// Seller states for the swap 19 | pub mod seller_swap; 20 | 21 | /// Buyer states for the swap 22 | pub mod buyer_swap; 23 | 24 | /// State machine implemenattion 25 | pub mod machine; 26 | -------------------------------------------------------------------------------- /libwallet/src/swap/multisig/error.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The vault713 Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | use crate::mwc_util::secp; 16 | 17 | /// Multisig error 18 | #[derive(Clone, Eq, PartialEq, Debug, thiserror::Error)] 19 | pub enum Error { 20 | /// Reveal phase error 21 | #[error("Multisig Invalid reveal")] 22 | Reveal, 23 | /// Not expected hash length, expected is 32 24 | #[error("Multisig Invalid hash length")] 25 | HashLength, 26 | /// Participant already exists 27 | #[error("Multisig Participant already exists")] 28 | ParticipantExists, 29 | /// Expected participant doesn't exist 30 | #[error("Multisig Participant doesn't exist")] 31 | ParticipantDoesntExist, 32 | /// Participant created in the wrong order 33 | #[error("Multisig Participant created in the wrong order")] 34 | ParticipantOrdering, 35 | /// Participant invalid 36 | #[error("Multisig Participant invalid")] 37 | ParticipantInvalid, 38 | /// Multisig incomplete 39 | #[error("Multisig incomplete")] 40 | MultiSigIncomplete, 41 | /// Common nonce missing 42 | #[error("Multisig Common nonce missing")] 43 | CommonNonceMissing, 44 | /// Round 1 missing field 45 | #[error("Multisig Round 1 missing field")] 46 | Round1Missing, 47 | /// Round 2 missing field 48 | #[error("Multisig Round 2 missing field")] 49 | Round2Missing, 50 | /// Secp error 51 | #[error("Multisig Secp: {0}")] 52 | Secp(#[from] secp::Error), 53 | } 54 | -------------------------------------------------------------------------------- /libwallet/src/swap/multisig/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The vault713 Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | mod error; 16 | /// Types used for multisig operations 17 | pub mod types; 18 | 19 | pub use self::error::Error; 20 | pub use self::types::{Builder, Hashed, ParticipantData}; 21 | -------------------------------------------------------------------------------- /libwallet/swap_test/context_buy.json: -------------------------------------------------------------------------------- 1 | { 2 | "multisig_key": "0200000000000000000000000000000000", 3 | "multisig_nonce": "f585ba0568ab60021823fe10e9f93b03e362f6f27a0d09327186864ba8f796fa", 4 | "lock_nonce": "5650c14e1433df16a66988849ea2a0a8c06af8499e8879bfda2d50b67bf3a4d0", 5 | "refund_nonce": "fdd88d1e66403878f521318e9327aff77ea5a4f2e2e732e129119fe33fe3fd0e", 6 | "redeem_nonce": "147cb76d6a16f7d652568d041169067a2e6a628ad4c81f721cf3f23e49431015", 7 | "role_context": { 8 | "Buyer": { 9 | "parent_key_id": "0200000000000000000000000000000000", 10 | "output": "0200000000000000010000000000000000", 11 | "redeem": "0200000000000000020000000000000000", 12 | "secondary_context": { 13 | "Btc": { 14 | "refund": "0200000000000000030000000000000000" 15 | } 16 | } 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /libwallet/swap_test/context_sell.json: -------------------------------------------------------------------------------- 1 | { 2 | "multisig_key": "0200000000000000000000000000000000", 3 | "multisig_nonce": "2c108b1174d63d8f443a2cf2b37341bf1917afa588136c6281d661e27a437b33", 4 | "lock_nonce": "958a46ed43f918561695ef264ae50e150135b0e3b3bbe8ac4f470f2a44053fc6", 5 | "refund_nonce": "b7227e72194bd335b771c2dc0c5b0ac15bc48f63627e27d120a3e9ad11c53adb", 6 | "redeem_nonce": "08c0f5f566263bd9e98d2fdefdfc5c180f3fe0c3b236af7ab14f9ed84530db7f", 7 | "role_context": { 8 | "Seller": { 9 | "parent_key_id": "0200000000000000000000000000000000", 10 | "inputs": [ 11 | [ 12 | "0200000000000000010000000000000000", 13 | null, 14 | 60000000000 15 | ], 16 | [ 17 | "0200000000000000020000000000000000", 18 | null, 19 | 60000000000 20 | ] 21 | ], 22 | "change_output": "0200000000000000030000000000000000", 23 | "change_amount": 20000000000, 24 | "refund_output": "0200000000000000040000000000000000", 25 | "secondary_context": { 26 | "Btc": { 27 | "cosign": "0200000000000000050000000000000000" 28 | } 29 | } 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /libwallet/swap_test/message_2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "4fc16adb-9f32-4441-b0c1-b4de076a1972", 3 | "inner": { 4 | "AcceptOffer": { 5 | "multisig": { 6 | "partial_commitment": "0929fbb62bc719998c1146fc78955ed210bb5bf474f968cbce230175e2585fb71c", 7 | "t_1": "025820002d7b0fda64d5f9c5ab58c6169f205da4bcf28d4b5091a3376bc3e357a9", 8 | "t_2": "02c48aeedbc41836ce47ed930655db0d1af7d484ab3d0c47029feddc97b6264904", 9 | "tau_x": "e6576c9e6521204c6bfe83cf14b02dd4ef4585ea8ccbd4e3a8f46b580b3af4dd" 10 | }, 11 | "redeem_public": "030654f6b3b4b54db0f2b066e651dcd67709444d2131d207ae5add2167596a091a", 12 | "lock_participant": { 13 | "id": "1", 14 | "public_blind_excess": "0329fbb62bc719998c1146fc78955ed210bb5bf474f968cbce230175e2585fb71c", 15 | "public_nonce": "025095c06816053b34317505e03ec069483ea0c17a865851560c06ce1a7c01b23b", 16 | "part_sig": "3bb2017c1ace060c565158867ac1a03e4869c03ee0057531343b051668c095508dbef5d8c85ffa7138968c04f8e9939bea2b696a0f40f884ec0f5b05e78e9c10", 17 | "message": null, 18 | "message_sig": null 19 | }, 20 | "refund_participant": { 21 | "id": "1", 22 | "public_blind_excess": "0229fbb62bc719998c1146fc78955ed210bb5bf474f968cbce230175e2585fb71c", 23 | "public_nonce": "02c5feea3b4566008a8075f9d16b9259853d7575719d5608a7160f8361a52efa96", 24 | "part_sig": "96fa2ea561830f16a708569d7175753d8559926bd1f975808a0066453beafec5ad56547ae452ba0abcec4f1d41d21e695dc4b08a984928ca68b7026362a7c9ee", 25 | "message": null, 26 | "message_sig": null 27 | } 28 | } 29 | }, 30 | "inner_secondary": { 31 | "BTC": { 32 | "AcceptOffer": { 33 | "refund": "03434496eb1554ad47c7ec5c267e902e1b71a03e189cc4fe9c83d9513e3a4e2e5a" 34 | } 35 | } 36 | } 37 | } -------------------------------------------------------------------------------- /libwallet/swap_test/message_3.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "4fc16adb-9f32-4441-b0c1-b4de076a1972", 3 | "inner": { 4 | "InitRedeem": { 5 | "redeem_slate": { 6 | "version_info": { 7 | "version": 2, 8 | "orig_version": 3, 9 | "block_header_version": 1 10 | }, 11 | "num_participants": 2, 12 | "id": "78aa5af1-048e-4c49-8776-a2e66d4a460c", 13 | "tx": { 14 | "offset": "90de4a3812c7b78e567548c86926820d838e7e0b43346b1ba63066cd5cc7d999", 15 | "body": { 16 | "inputs": [ 17 | { 18 | "features": "Plain", 19 | "commit": "084ff3703ce3ac1354759727878d0868b09867b192dcdf7f0be043d2a6d35e28fc" 20 | } 21 | ], 22 | "outputs": [ 23 | { 24 | "features": "Plain", 25 | "commit": "08a0ba5aff8954f117ab2b1f475944d726b5e36fe474f1ca6db79630f2da5b9c85", 26 | "proof": "a7d92bfa3ccc026828c70eff08eefd9799ac1719f48fb16214b6b774aeac354af43b859f2f0a752e19f916b14b027b98db03bad04353cc925b7cbae64518f8360fa19ae8c4d46ef0148d0b3c303164c2d5936ccb511094fd449e74bcc55ca8f9433b79c280b715ffe6753111e4a662698d8a9cbeed3e72e48b774f9177d2a7afc67b47c9d52efee4ef78abff6a81c559a6e2e46535d07b76c2e8af8987e153128eb870e7e4b4db5e47908f4a92f100ca8412a6575352fc9cdd3370004cfa349a2bc1b0993212deebd9bb8c2a91a67a2e27e12214fc7e5d477a5db3fc11046b295314a82648cbf77db2108c1588de614b8cd5e15481a895e13c62fd24b923701ca7423eaad126046e0ea3f47c4e8ffef3f8b4ab70d215fe41103ed333b0c1b263dcdbde84ff88735e7841a0ea9aab1db997f88dacbff8f889a3708d27d8df7f28bdd0b585cb8420e411a96b9411b3b83e411ffa891e317bda2ed844c729fe346e0433036f0b70303bea1c1e307bb2c56ba561852d3ba01618c49507356184395a73a607c8b6647a9fc4fc31914906be890bd37b9001e0d200bed31ba1b0fc2a8b984780f037d623709f345f1a454f76cd27314d9b1b9e739c5756c8ffcc12ebcccf73cb4097005941a7394a86a27fd512f1ded7747b2a4c86b2048f1e31952d3f30e8f64d5734422242492b44b09eb73830b6a42467cf6a4923d967e54abc190a2d414a2b91f1268b61347b99c923167321e0e159367e27a7e8d08e73a38a9279c1cb4fab1a3a171a72b70c3a931feadf4b0909b76d19707048625ac0e5b5f416a90d8312b4b2db30ea12b4a33ab10db42ad45ffd1d3a3fe1d02922cdd90e0f02abfa5a5eebf7c0d1e756ffe6456b1e5f0e38ec66920f3231ba9d6fb061e276949cbe588a9869c48dd6c88191094a9ecc6dc13d3d17b4ee65d6e1d1da94944aef421dc4" 27 | } 28 | ], 29 | "kernels": [ 30 | { 31 | "features": "Plain", 32 | "fee": "4000000", 33 | "lock_height": "0", 34 | "excess": "000000000000000000000000000000000000000000000000000000000000000000", 35 | "excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" 36 | } 37 | ] 38 | } 39 | }, 40 | "amount": "99996000000", 41 | "fee": "4000000", 42 | "height": "300002", 43 | "lock_height": "0", 44 | "coin_type": "mwc", 45 | "network_type": "floonet", 46 | "participant_data": [ 47 | { 48 | "id": "0", 49 | "public_blind_excess": "0267084e1cd57ab8bea0a631397d6addced43d13b154d9bbc39850b82a59a4940c", 50 | "public_nonce": "0252cb796558103905ccf66987645382a19b8b2abebe501f63c1862cbe152da0fd", 51 | "part_sig": null, 52 | "message": null, 53 | "message_sig": null 54 | }, 55 | { 56 | "id": "1", 57 | "public_blind_excess": "025a6d71e906d11a3eaa0cef3022c401a258bd08e4d57913ed7bdd0630d18baf68", 58 | "public_nonce": "02ddb6a6e27a05ddfe956598b806e83a6566851f85809ec69ce9a95452a0a543c2", 59 | "part_sig": null, 60 | "message": null, 61 | "message_sig": null 62 | } 63 | ] 64 | }, 65 | "adaptor_signature": "c243a5a05254a9e99cc69e80851f8566653ae806b8986595fedd057ae2a6b6dd20cea0957af2f764ab045d1177d4fa0a353428dcb8f572a9ca4b3007bcac9a0a" 66 | } 67 | }, 68 | "inner_secondary": "Empty" 69 | } -------------------------------------------------------------------------------- /libwallet/swap_test/message_4.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "4fc16adb-9f32-4441-b0c1-b4de076a1972", 3 | "inner": { 4 | "Redeem": { 5 | "redeem_participant": { 6 | "id": "0", 7 | "public_blind_excess": "0267084e1cd57ab8bea0a631397d6addced43d13b154d9bbc39850b82a59a4940c", 8 | "public_nonce": "0252cb796558103905ccf66987645382a19b8b2abebe501f63c1862cbe152da0fd", 9 | "part_sig": "fda02d15be2c86c1631f50bebe2a8b9ba18253648769f6cc053910586579cb52bd1db78bcfe5f6e74a7be40ce106ffaf60827a1e691796f8f01f2074c532cf6a", 10 | "message": null, 11 | "message_sig": null 12 | } 13 | } 14 | }, 15 | "inner_secondary": "Empty" 16 | } -------------------------------------------------------------------------------- /libwallet/tests/slate_versioning.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! core::libtx specific tests 16 | //use mwc_wallet_libwallet::Slate; 17 | 18 | // test all slate conversions 19 | /* TODO: Turn back on upon release of new slate version 20 | #[test] 21 | fn slate_conversions() { 22 | // Test V0 to V2 23 | let v0 = include_str!("slates/v0.slate"); 24 | let res = Slate::deserialize_upgrade(&v0); 25 | assert!(res.is_ok()); 26 | // should serialize as latest 27 | let mut res = res.unwrap(); 28 | assert_eq!(res.version_info.orig_version, 0); 29 | res.version_info.orig_version = 2; 30 | let s = serde_json::to_string(&res); 31 | assert!(s.is_ok()); 32 | let s = s.unwrap(); 33 | let v = Slate::parse_slate_version(&s); 34 | assert!(v.is_ok()); 35 | assert_eq!(v.unwrap(), 2); 36 | println!("v0 -> v2: {}", s); 37 | 38 | // Test V1 to V2 39 | let v1 = include_str!("slates/v1.slate"); 40 | let res = Slate::deserialize_upgrade(&v1); 41 | assert!(res.is_ok()); 42 | // should serialize as latest 43 | let mut res = res.unwrap(); 44 | assert_eq!(res.version_info.orig_version, 1); 45 | res.version_info.orig_version = 2; 46 | let s = serde_json::to_string(&res); 47 | assert!(s.is_ok()); 48 | let s = s.unwrap(); 49 | let v = Slate::parse_slate_version(&s); 50 | assert!(v.is_ok()); 51 | assert_eq!(v.unwrap(), 2); 52 | println!("v1 -> v2: {}", s); 53 | 54 | // V2 -> V2, check version 55 | let v2 = include_str!("slates/v2.slate"); 56 | let res = Slate::deserialize_upgrade(&v2); 57 | assert!(res.is_ok()); 58 | let res = res.unwrap(); 59 | assert_eq!(res.version_info.orig_version, 2); 60 | let s = serde_json::to_string(&res); 61 | assert!(s.is_ok()); 62 | let s = s.unwrap(); 63 | let v = Slate::parse_slate_version(&s); 64 | assert!(v.is_ok()); 65 | assert_eq!(v.unwrap(), 2); 66 | 67 | // Downgrade to V1 68 | let v2 = include_str!("slates/v2.slate"); 69 | let res = Slate::deserialize_upgrade(&v2); 70 | assert!(res.is_ok()); 71 | let mut res = res.unwrap(); 72 | // downgrade 73 | res.version_info.orig_version = 1; 74 | let s = serde_json::to_string(&res); 75 | assert!(s.is_ok()); 76 | let s = s.unwrap(); 77 | let v = Slate::parse_slate_version(&s); 78 | assert!(v.is_ok()); 79 | assert_eq!(v.unwrap(), 1); 80 | println!("v2 -> v1: {}", s); 81 | 82 | // Downgrade to V0 83 | let v2 = include_str!("slates/v2.slate"); 84 | let res = Slate::deserialize_upgrade(&v2); 85 | assert!(res.is_ok()); 86 | let mut res = res.unwrap(); 87 | // downgrade 88 | res.version_info.orig_version = 0; 89 | let s = serde_json::to_string(&res); 90 | assert!(s.is_ok()); 91 | let s = s.unwrap(); 92 | let v = Slate::parse_slate_version(&s); 93 | assert!(v.is_ok()); 94 | assert_eq!(v.unwrap(), 0); 95 | println!("v2 -> v0: {}", s); 96 | } 97 | */ 98 | -------------------------------------------------------------------------------- /libwallet/tests/slates/v2.slate: -------------------------------------------------------------------------------- 1 | { 2 | "version_info": { 3 | "version": 2, 4 | "orig_version": 3, 5 | "block_header_version": 1 6 | }, 7 | "num_participants": 2, 8 | "id": "e0c69803-db50-40d9-a968-496e86660cd4", 9 | "tx": { 10 | "offset": "a853afebf15d8c111f654059940945b4782c38660397257707b53ebfdb403a52", 11 | "body": { 12 | "inputs": [ 13 | { 14 | "features": "Plain", 15 | "commit": "09d304aed6300f8124eb8b2d46cc1e0a7b7a9b9042b9cb35e020dd9552df9c697c" 16 | }, 17 | { 18 | "features": "Plain", 19 | "commit": "09d3cc915dc317485dc8bbf5ec4669a40bb9d3300c96df3384d116ddad498d0db1" 20 | } 21 | ], 22 | "outputs": [ 23 | { 24 | "features": "Plain", 25 | "commit": "08d3453eb5ce35a1b6bbc2a7a9afe32483774c011f9975f42393468fa5cd4349a7", 26 | "proof": "db206834c022eec1f346a67b571941f1b6867ae4bd8189ca064b690b32367e454a4a5add51761c472b0e0994ce7f00578bc06ae7b9afdf8ce2118546771976d900464214d3b831fe74a94876980a928315afb5c2af018f5d595e56fd740658b0c4f2d4f463e401cbec2704b31005cd8d7d87458290a3668cc2e82c2b0867d991072544f9e8c805056c97ff66cc052cf2a9666768d0d68acdc6ea1fc80fb9b5e6e19366c7b49ada38b368c0c3e3f73977df003f0c6744737b31b058c7d4e2766e97ee04147ef04be22906f087842205813c7d817598c689c840087d35cc9ce9a98f52e68c66bdde0521acf814737efd072654728f418e6494a7eb7fa6305ec7d572abb91d3bfabf7215e77e0c9cf33769572ff9a8671a24e0a04302e6ac5cee9928ec11d7c9861ed18718142a1563967955e428e4134c6dde88bdbea11248ae99d784a56592a065122948b2c2fb8be25c119345b9fa7db2efbdfcf846e9ba47efff3d0024bdb998e93bcabe1a00222ba36b88ec4f7c2a2151bf00b225f6a14b4de66658daecaa219813f51a9239eec961c6713106b64c4f1ff851e54795220ee3cdc59531f0acc050e17c848b21b916b571b2f6b093fccec046587d0a1718c82bd7a78e22223fe1484dec841820139950dce84c97659b0eac1bfa5fce85d5602f480d714dcab1459c4f29e2746bccb4494d800935ddc630f53257649f1544702003a583d55422e957192faebffcb8d883ec6bb2132c86249d6b50edae84f3c06842b2714267249c8df58e2edc3aca69dff66ee32fb5d93db9156df373ab51df2c094742517b46ff95298caec3464151ea91c8a8fe74bb60ffb94c7c974aa6cb2e47dd1ee05f471e2d2f0b555efe17302769139760bc110c979453f7bfab43b3f3cba4d94c8a5eeb58264bb5c16de6acbbc9c56cb069e7e1ac1f7838d0a6424017b8d563" 27 | } 28 | ], 29 | "kernels": [ 30 | { 31 | "features": "HeightLocked", 32 | "fee": "7000000", 33 | "lock_height": "70194", 34 | "excess": "000000000000000000000000000000000000000000000000000000000000000000", 35 | "excess_sig": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" 36 | } 37 | ] 38 | } 39 | }, 40 | "amount": "84825921007", 41 | "fee": "7000000", 42 | "height": "70194", 43 | "lock_height": "70194", 44 | "participant_data": [ 45 | { 46 | "id": "0", 47 | "public_blind_excess": "0391f8fc74bb5ff4de373352e7dee00860d4fb78ed7a99765585af980d8a31c615", 48 | "public_nonce": "0206562c21a7f3a003622722ee93c4ecbbecead4a6ad8ee5d930b51ca4a6ca6d01", 49 | "part_sig": null, 50 | "message": null, 51 | "message_sig": null 52 | } 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | edition = "2018" 3 | -------------------------------------------------------------------------------- /src/build/build.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Build hooks to spit out version+build time info 17 | 18 | use built; 19 | use std::env; 20 | use std::path::{Path, PathBuf}; 21 | use std::process::Command; 22 | 23 | fn main() { 24 | // Setting up git hooks in the project: rustfmt and so on. 25 | let git_hooks = format!( 26 | "git config core.hooksPath {}", 27 | PathBuf::from("./.hooks").to_str().unwrap() 28 | ); 29 | 30 | if cfg!(target_os = "windows") { 31 | Command::new("cmd") 32 | .args(&["/C", &git_hooks]) 33 | .output() 34 | .expect("failed to execute git config for hooks"); 35 | } else { 36 | Command::new("sh") 37 | .args(&["-c", &git_hooks]) 38 | .output() 39 | .expect("failed to execute git config for hooks"); 40 | } 41 | 42 | // build and versioning information 43 | let mut opts = built::Options::default(); 44 | opts.set_dependencies(true); 45 | let out_dir_path = format!("{}{}", env::var("OUT_DIR").unwrap(), "/built.rs"); 46 | // don't fail the build if something's missing, may just be cargo release 47 | let _ = built::write_built_file_with_opts( 48 | &opts, 49 | Path::new(env!("CARGO_MANIFEST_DIR")), 50 | Path::new(&out_dir_path), 51 | ); 52 | } 53 | -------------------------------------------------------------------------------- /src/cli/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod cli; 17 | 18 | pub use cli::command_loop; 19 | -------------------------------------------------------------------------------- /src/cmd/mod.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | mod wallet; 17 | pub mod wallet_args; 18 | 19 | pub use self::wallet::wallet_command; 20 | -------------------------------------------------------------------------------- /src/cmd/wallet.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | use crate::cmd::wallet_args; 17 | use crate::config::GlobalWalletConfig; 18 | use clap::ArgMatches; 19 | use mwc_wallet_libwallet::NodeClient; 20 | use std::thread; 21 | use std::time::Duration; 22 | 23 | pub(crate) const MIN_COMPAT_NODE_VERSION: &str = "5.3.0"; 24 | 25 | pub fn wallet_command( 26 | wallet_args: &ArgMatches<'_>, 27 | config: GlobalWalletConfig, 28 | mut node_client: C, 29 | ) -> i32 30 | where 31 | C: NodeClient + 'static, 32 | { 33 | // just get defaults from the global config 34 | let wallet_config = config.members.clone().unwrap().wallet; 35 | 36 | let tor_config = config.members.clone().unwrap().tor; 37 | let mqs_config = config.members.unwrap().mqs; 38 | 39 | // Check the node version info, and exit with report if we're not compatible 40 | let global_wallet_args = wallet_args::parse_global_args(&wallet_config, &wallet_args) 41 | .expect("Can't read configuration file"); 42 | node_client.set_node_api_secret(global_wallet_args.node_api_secret.clone()); 43 | 44 | // Note, we can't use node here because 'api_server_address' argument needs to be applied first. 45 | // That happens inside wallet_command 46 | 47 | let res = wallet_args::wallet_command( 48 | wallet_args, 49 | wallet_config, 50 | tor_config, 51 | mqs_config, 52 | node_client, 53 | false, 54 | |_| {}, 55 | ); 56 | 57 | // we need to give log output a chance to catch up before exiting 58 | thread::sleep(Duration::from_millis(100)); 59 | 60 | if let Err(e) = res { 61 | println!("Wallet command failed: {}", e); 62 | 1 63 | } else { 64 | println!( 65 | "Command '{}' completed successfully", 66 | wallet_args.subcommand().0 67 | ); 68 | 0 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #[macro_use] 16 | extern crate lazy_static; 17 | #[macro_use] 18 | extern crate clap; 19 | 20 | //#[macro_use] 21 | //extern crate log; 22 | 23 | use mwc_wallet_config as config; 24 | use mwc_wallet_util::mwc_api as api; 25 | use mwc_wallet_util::mwc_util as util; 26 | 27 | mod cli; 28 | pub mod cmd; 29 | -------------------------------------------------------------------------------- /tests/data/v2_reqs/init_send_tx.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "init_send_tx", 4 | "params": { 5 | "args": { 6 | "src_acct_name": null, 7 | "amount": "600000000000", 8 | "minimum_confirmations": 2, 9 | "max_outputs": 500, 10 | "num_change_outputs": 1, 11 | "selection_strategy_is_use_all": true, 12 | "message": "my message", 13 | "target_slate_version": null, 14 | "payment_proof_recipient_address": null, 15 | "ttl_blocks": null, 16 | "address": null, 17 | "estimate_only": false, 18 | "send_args": null 19 | } 20 | }, 21 | "id": 1 22 | } 23 | 24 | -------------------------------------------------------------------------------- /tests/data/v2_reqs/retrieve_info.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "retrieve_summary_info", 4 | "params": [ 5 | true, 6 | 1 7 | ], 8 | "id": 1 9 | } 10 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/change_password.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "change_password", 4 | "params": { 5 | "name": null, 6 | "old": "passwoid", 7 | "new": "password" 8 | }, 9 | "id": 1 10 | } 11 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/close_wallet.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "close_wallet", 4 | "params": { 5 | "name": null 6 | }, 7 | "id": 1 8 | } 9 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/create_config.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "create_config", 4 | "params": { 5 | "chain_type": "AutomatedTesting", 6 | "wallet_config": null, 7 | "logging_config": null, 8 | "tor_config": null, 9 | "mqs_config": null 10 | }, 11 | "id": 1 12 | } 13 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/create_wallet.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "create_wallet", 4 | "params": { 5 | "name": null, 6 | "mnemonic": null, 7 | "mnemonic_length": 32, 8 | "password": "passwoid" 9 | }, 10 | "id": 1 11 | } 12 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/create_wallet_invalid_mn.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "create_wallet", 4 | "params": { 5 | "name": null, 6 | "mnemonic": "this is not valid", 7 | "mnemonic_length": 32, 8 | "password": "passwoid" 9 | }, 10 | "id": 1 11 | } 12 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/create_wallet_valid_mn.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "create_wallet", 4 | "params": { 5 | "name": null, 6 | "mnemonic": "fat twenty mean degree forget shell check candy immense awful flame next during february bulb bike sun wink theory day kiwi embrace peace lunch", 7 | "mnemonic_length": 32, 8 | "password": "passwoid" 9 | }, 10 | "id": 1 11 | } 12 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/delete_wallet.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "delete_wallet", 4 | "params": { 5 | "name": null 6 | }, 7 | "id": 1 8 | } 9 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/get_top_level.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "get_top_level_directory", 4 | "params": { 5 | }, 6 | "id": 1 7 | } 8 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/init_secure_api.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "init_secure_api", 4 | "params": { 5 | "ecdh_pubkey": "03b3c18c9a38783d105e238953b1638b021ba7456d87a5c085b3bdb75777b4c490" 6 | }, 7 | "id": 1 8 | } 9 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/init_send_tx.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "init_send_tx", 4 | "params": { 5 | "token": null, 6 | "args": { 7 | "src_acct_name": null, 8 | "amount": "600000000000", 9 | "amount_includes_fee": false, 10 | "minimum_confirmations": 2, 11 | "max_outputs": 500, 12 | "num_change_outputs": 1, 13 | "selection_strategy_is_use_all": true, 14 | "message": "my message", 15 | "target_slate_version": null, 16 | "payment_proof_recipient_address": null, 17 | "ttl_blocks": null, 18 | "send_args": null 19 | } 20 | }, 21 | "id": 1 22 | } 23 | 24 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/open_wallet.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "open_wallet", 4 | "params": { 5 | "name": null, 6 | "password": "passwoid" 7 | }, 8 | "id": 1 9 | } 10 | -------------------------------------------------------------------------------- /tests/data/v3_reqs/retrieve_info.req.json: -------------------------------------------------------------------------------- 1 | { 2 | "jsonrpc": "2.0", 3 | "method": "retrieve_summary_info", 4 | "params": { 5 | "token": null, 6 | "refresh_from_node": true, 7 | "minimum_confirmations": 1 8 | }, 9 | "id": 1 10 | } 11 | -------------------------------------------------------------------------------- /tests/owner_v2_sanity.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #[macro_use] 16 | extern crate clap; 17 | 18 | #[macro_use] 19 | extern crate log; 20 | 21 | extern crate mwc_wallet; 22 | 23 | use mwc_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy}; 24 | use std::ops::DerefMut; 25 | use std::sync::Arc; 26 | 27 | use clap::App; 28 | use std::thread; 29 | use std::time::Duration; 30 | 31 | use mwc_wallet_impls::DefaultLCProvider; 32 | use mwc_wallet_util::mwc_core::global; 33 | use mwc_wallet_util::mwc_keychain::ExtKeychain; 34 | 35 | #[macro_use] 36 | mod common; 37 | use common::RetrieveSummaryInfoResp; 38 | use common::{ 39 | clean_output_dir, execute_command, initial_setup_wallet, instantiate_wallet, send_request, 40 | setup, 41 | }; 42 | use mwc_wallet_util::mwc_core::core::Transaction; 43 | use mwc_wallet_util::mwc_util::Mutex; 44 | 45 | #[test] 46 | fn owner_v2_sanity() -> Result<(), mwc_wallet_controller::Error> { 47 | // For windows we can't run it because of the leaks. And we dont want to see bunch of warnings as well 48 | #[cfg(target_os = "windows")] 49 | if true { 50 | return Ok(()); 51 | } 52 | 53 | let test_dir = "target/test_output/owner_v2_sanity"; 54 | setup(test_dir); 55 | global::set_local_chain_type(global::ChainTypes::AutomatedTesting); 56 | // Running update thread, we can't set local to it... 57 | global::init_global_chain_type(global::ChainTypes::AutomatedTesting); 58 | 59 | let tx_pool: Arc>> = Arc::new(Mutex::new(Vec::new())); 60 | setup_proxy!(test_dir, tx_pool, chain, wallet1, client1, mask1, wallet2, client2, _mask2); 61 | 62 | // add some blocks manually 63 | let bh = 10u64; 64 | let _ = test_framework::award_blocks_to_wallet( 65 | &chain, 66 | wallet1.clone(), 67 | mask1, 68 | bh as usize, 69 | false, 70 | tx_pool.lock().deref_mut(), 71 | ); 72 | let client1_2 = client1.clone(); 73 | 74 | // run the owner listener on wallet 1 75 | let arg_vec = vec!["mwc-wallet", "-p", "password", "owner_api"]; 76 | // Set running 77 | thread::spawn(move || { 78 | global::set_local_chain_type(global::ChainTypes::AutomatedTesting); 79 | let yml = load_yaml!("../src/bin/mwc-wallet.yml"); 80 | let app = App::from_yaml(yml); 81 | execute_command(&app, test_dir, "wallet1", &client1, arg_vec.clone()).unwrap(); 82 | }); 83 | 84 | // run the foreign listener for wallet 2 85 | let arg_vec = vec![ 86 | "mwc-wallet", 87 | "-p", 88 | "password", 89 | "listen", 90 | "-l", 91 | "23415", 92 | "-n", 93 | ]; 94 | // Set owner listener running 95 | thread::spawn(move || { 96 | global::set_local_chain_type(global::ChainTypes::AutomatedTesting); 97 | let yml = load_yaml!("../src/bin/mwc-wallet.yml"); 98 | let app = App::from_yaml(yml); 99 | execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone()).unwrap(); 100 | }); 101 | 102 | thread::sleep(Duration::from_millis(1000)); 103 | 104 | // 1) Send simple retrieve_info request to owner listener 105 | let req = include_str!("data/v2_reqs/retrieve_info.req.json"); 106 | let res = send_request(1, "http://127.0.0.1:3420/v2/owner", req)?; 107 | assert!(res.is_ok()); 108 | let value: RetrieveSummaryInfoResp = res.unwrap(); 109 | assert_eq!(value.1.amount_currently_spendable, 16_666_666_660); // mwc: 420000000000 110 | println!("Response 1: {:?}", value); 111 | 112 | // 2) Send to wallet 2 foreign listener 113 | let arg_vec = vec![ 114 | "mwc-wallet", 115 | "-p", 116 | "password", 117 | "send", 118 | "-d", 119 | "http://127.0.0.1:23415", 120 | "2", // mwc: 10 Only one block reward is spendable 121 | ]; 122 | let yml = load_yaml!("../src/bin/mwc-wallet.yml"); 123 | let app = App::from_yaml(yml); 124 | let res = execute_command(&app, test_dir, "wallet1", &client1_2, arg_vec.clone()); 125 | println!("Response 2: {:?}", res); 126 | assert!(res.is_ok()); 127 | 128 | clean_output_dir(test_dir); 129 | Ok(()) 130 | } 131 | -------------------------------------------------------------------------------- /tests/tor_dev_helper.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | #[macro_use] 16 | extern crate clap; 17 | 18 | #[macro_use] 19 | extern crate log; 20 | 21 | extern crate mwc_wallet; 22 | 23 | use mwc_wallet_impls::test_framework::{self, LocalWalletClient, WalletProxy}; 24 | use std::ops::DerefMut; 25 | use std::sync::Arc; 26 | 27 | use clap::App; 28 | use std::thread; 29 | use std::time::Duration; 30 | 31 | use mwc_wallet_impls::DefaultLCProvider; 32 | use mwc_wallet_util::mwc_keychain::ExtKeychain; 33 | 34 | use mwc_wallet_util::mwc_util as util; 35 | 36 | #[macro_use] 37 | mod common; 38 | use common::{execute_command, initial_setup_wallet, instantiate_wallet, setup_global_chain_type}; 39 | use mwc_wallet_util::mwc_core::core::Transaction; 40 | use mwc_wallet_util::mwc_util::Mutex; 41 | // Development testing helper for tor/socks investigation. 42 | // Not (yet) to be run as part of automated testing 43 | 44 | fn setup_no_clean() { 45 | util::init_test_logger(); 46 | setup_global_chain_type(); 47 | } 48 | 49 | #[ignore] 50 | #[test] 51 | fn socks_tor() -> Result<(), mwc_wallet_controller::Error> { 52 | // For windows we can't run it because of the leaks. And we dont want to see bunch of warnings as well 53 | #[cfg(target_os = "windows")] 54 | if true { 55 | return Ok(()); 56 | } 57 | 58 | let test_dir = "target/test_output/socks_tor"; 59 | let yml = load_yaml!("../src/bin/mwc-wallet.yml"); 60 | let app = App::from_yaml(yml); 61 | setup_no_clean(); 62 | 63 | let tx_pool: Arc>> = Arc::new(Mutex::new(Vec::new())); 64 | setup_proxy!(test_dir, tx_pool, chain, wallet1, client1, mask1, wallet2, client2, _mask2); 65 | 66 | // Tor should be running at this point for wallet 2, with a hidden service 67 | // bound to the listening port 53415. By default, tor will also be running 68 | // a socks proxy lister at 127.0.0.1 9050 (both wallets can use for now) 69 | // 70 | // Relevant torrc config: 71 | // HiddenServiceDir ./hidden_service/ 72 | // HiddenServicePort 80 127.0.0.1:53415 73 | // 74 | // tor -f torrc 75 | 76 | // Substitute whatever onion address has been created 77 | let onion_address = "2a6at2obto3uvkpkitqp4wxcg6u36qf534eucbskqciturczzc5suyid"; 78 | 79 | // run the foreign listener for wallet 2 80 | let arg_vec = vec!["mwc-wallet", "-p", "password", "listen"]; 81 | // Set owner listener running 82 | thread::spawn(move || { 83 | let yml = load_yaml!("../src/bin/mwc-wallet.yml"); 84 | let app = App::from_yaml(yml); 85 | execute_command(&app, test_dir, "wallet2", &client2, arg_vec.clone()).unwrap(); 86 | }); 87 | 88 | // dumb pause for now, hidden service should already be running 89 | thread::sleep(Duration::from_millis(3000)); 90 | 91 | // mine into wallet 1 a bit 92 | let bh = 5u64; 93 | let _ = test_framework::award_blocks_to_wallet( 94 | &chain, 95 | wallet1.clone(), 96 | mask1, 97 | bh as usize, 98 | false, 99 | tx_pool.lock().deref_mut(), 100 | ); 101 | 102 | // now, test send from wallet 1 over tor 103 | let arg_vec = vec![ 104 | "mwc-wallet", 105 | "-p", 106 | "password", 107 | "send", 108 | "-c", 109 | "2", 110 | "-d", 111 | onion_address, 112 | "10", 113 | ]; 114 | execute_command(&app, test_dir, "wallet1", &client1, arg_vec)?; 115 | 116 | Ok(()) 117 | } 118 | -------------------------------------------------------------------------------- /util/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "mwc_wallet_util" 3 | version = "5.3.6" 4 | authors = ["Mwc Developers "] 5 | description = "Util, for generic utilities and to re-export mwc crates" 6 | license = "Apache-2.0" 7 | repository = "https://github.com/mwcproject/mwc-wallet" 8 | keywords = [ "crypto", "mwc", "mimblewimble" ] 9 | workspace = ".." 10 | edition = "2018" 11 | 12 | [dependencies] 13 | rand = "0.6" 14 | serde = "1" 15 | serde_derive = "1" 16 | ed25519-dalek = "1.0.0-pre.4" 17 | data-encoding = "2" 18 | sha3 = "0.8" 19 | lazy_static = "1.4" 20 | tokio = { version = "0.2", features = ["full"] } 21 | thiserror = "1" 22 | 23 | # For Release 24 | mwc_core = { git = "https://github.com/mwcproject/mwc-node", version="5.3.8", tag = "5.3.8.1" } 25 | mwc_keychain = { git = "https://github.com/mwcproject/mwc-node", version="5.3.8", tag = "5.3.8.1" } 26 | mwc_chain = { git = "https://github.com/mwcproject/mwc-node", version="5.3.8", tag = "5.3.8.1" } 27 | mwc_util = { git = "https://github.com/mwcproject/mwc-node", version="5.3.8", tag = "5.3.8.1" } 28 | mwc_api = { git = "https://github.com/mwcproject/mwc-node", version="5.3.8", tag = "5.3.8.1" } 29 | mwc_store = { git = "https://github.com/mwcproject/mwc-node", version="5.3.8", tag = "5.3.8.1" } 30 | mwc_p2p = { git = "https://github.com/mwcproject/mwc-node", version="5.3.8", tag = "5.3.8.1" } 31 | 32 | # For bleeding edge 33 | #mwc_core = { git = "https://github.com/mwcproject/mwc-node", branch = "5.3.100" } 34 | #mwc_keychain = { git = "https://github.com/mwcproject/mwc-node", branch = "5.3.100" } 35 | #mwc_chain = { git = "https://github.com/mwcproject/mwc-node", branch = "5.3.100" } 36 | #mwc_util = { git = "https://github.com/mwcproject/mwc-node", branch = "5.3.100" } 37 | #mwc_api = { git = "https://github.com/mwcproject/mwc-node", branch = "5.3.100" } 38 | #mwc_store = { git = "https://github.com/mwcproject/mwc-node", branch = "5.3.100" } 39 | #mwc_p2p = { git = "https://github.com/mwcproject/mwc-node", branch = "5.3.100" } 40 | 41 | # For local testing 42 | #mwc_core = { path = "../../mwc-node/core"} 43 | #mwc_keychain = { path = "../../mwc-node/keychain"} 44 | #mwc_chain = { path = "../../mwc-node/chain"} 45 | #mwc_util = { path = "../../mwc-node/util"} 46 | #mwc_api = { path = "../../mwc-node/api"} 47 | #mwc_store = { path = "../../mwc-node/store"} 48 | #mwc_p2p = { path = "../../mwc-node/p2p"} 49 | 50 | [dev-dependencies] 51 | pretty_assertions = "0.6" 52 | -------------------------------------------------------------------------------- /util/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2019 The Grin Developers 2 | // Copyright 2024 The Mwc Developers 3 | // 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // You may obtain a copy of the License at 7 | // 8 | // http://www.apache.org/licenses/LICENSE-2.0 9 | // 10 | // Unless required by applicable law or agreed to in writing, software 11 | // distributed under the License is distributed on an "AS IS" BASIS, 12 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | // See the License for the specific language governing permissions and 14 | // limitations under the License. 15 | 16 | //! Utilities and re-exports 17 | 18 | #![deny(non_upper_case_globals)] 19 | #![deny(non_camel_case_types)] 20 | #![deny(non_snake_case)] 21 | #![deny(unused_mut)] 22 | #![warn(missing_docs)] 23 | 24 | #[macro_use] 25 | extern crate serde_derive; 26 | 27 | mod ov3; 28 | mod tokio_runtime; 29 | 30 | pub use ov3::OnionV3Address; 31 | pub use ov3::OnionV3Error as OnionV3AddressError; 32 | pub use tokio_runtime::RUNTIME; 33 | 34 | pub use mwc_api; 35 | pub use mwc_chain; 36 | pub use mwc_core; 37 | pub use mwc_keychain; 38 | pub use mwc_p2p; 39 | pub use mwc_store; 40 | pub use mwc_util; 41 | -------------------------------------------------------------------------------- /util/src/tokio_runtime.rs: -------------------------------------------------------------------------------- 1 | // Copyright 2024 The MWC Developers 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //! Utilities and re-exports 16 | use lazy_static::lazy_static; 17 | use std::sync::{Arc, Mutex}; 18 | 19 | use tokio::runtime::{Builder, Runtime}; 20 | 21 | lazy_static! { 22 | /// Note: RUNTIME can be used in multiple crates, that is why it is declared in utils. 23 | /// Global Tokio runtime. 24 | /// Needs a `Mutex` because `Runtime::block_on` requires mutable access. 25 | /// Tokio v0.3 requires immutable self, but we are waiting on upstream 26 | /// updates before we can upgrade. 27 | /// See: https://github.com/seanmonstar/reqwest/pull/1076 28 | pub static ref RUNTIME: Arc> = Arc::new(Mutex::new( 29 | Builder::new() 30 | .threaded_scheduler() 31 | .enable_all() 32 | .build() 33 | .unwrap() 34 | )); 35 | } 36 | --------------------------------------------------------------------------------