Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[iOS] Bitcoin account creation #22737

Merged
merged 6 commits into from
Apr 2, 2024
Merged

Conversation

nuo-xu
Copy link
Contributor

@nuo-xu nuo-xu commented Mar 22, 2024

Resolves brave/brave-browser#36811

Submitter Checklist:

  • I confirm that no security/privacy review is needed and no other type of reviews are needed, or that I have requested them
  • There is a ticket for my issue
  • Used Github auto-closing keywords in the PR description above
  • Wrote a good PR/commit description
  • Squashed any review feedback or "fixup" commits before merge, so that history is a record of what happened in the repo, not your PR
  • Added appropriate labels (QA/Yes or QA/No; release-notes/include or release-notes/exclude; OS/...) to the associated issue
  • Checked the PR locally:
    • npm run test -- brave_browser_tests, npm run test -- brave_unit_tests wiki
    • npm run presubmit wiki, npm run gn_check, npm run tslint
  • Ran git rebase master (if needed)

Reviewer Checklist:

  • A security review is not needed, or a link to one is included in the PR description
  • New files have MPL-2.0 license header
  • Adequate test coverage exists to prevent regressions
  • Major classes, functions and non-trivial code blocks are well-commented
  • Changes in component dependencies are properly reflected in gn
  • Code follows the style guide
  • Test plan is specified in PR before merging

After-merge Checklist:

Test Plan:

In order to test this PR, you need to set up a feature flag in two ways:

  1. If you are building this PR locally on your Xcode, duplicate Debug scheme and add --enable-features=BraveWalletBitcoin in your Arguments Passed On Launch, then build.
  2. If you are testing using a TF build. Add BraveWalletBitcoin under Settings/BraveCore Switches/Enable Features (Enable Enable Features and a fresh launch is required)

Note: Bitcoin balance fetching will be addressed in a separate issue brave/brave-browser#36966
All BTC balance will be displayed as 0 value. Send BTC will be addressed in a separate issue brave/brave-browser#36968

Test BTC default user asset:

  1. Fresh install Brave
  2. Create a new wallet
  3. Observe BTC on Mainnet is displayed in Portfolio
  4. Go to BTC on mainnet asset details
  5. Observe BTC price and price history is rendered
  6. Go to wallet setting to turn on test networks
  7. Select Bitcoin Testnet in filter and save
  8. Observe BTC on Testnet is displayed in Portfolio
    1. Go to BTC on mainnet asset details
  9. Observe BTC price and price history is rendered

Test Bitcoin Account creation

  1. Go to BTC on mainnet asset details
  2. Tap Send
  3. You will be prompt with an alert since there is no Bitcoin Account yet
  4. Tap yes to create a new bitcoin account
  5. Observe Bitcoin Mainnet is pre-selected and cannot be changed
  6. Observe there is no import private key section
  7. Input an account name and tap Add
  8. Account creation screen will be dismissed, and you will be redirect to send screen.
  9. Go to Account Tab
  10. Observe the newly created bitcoin account is displayed
  11. Tap the bitcoin account to go to the account details
  12. Observe there is no address displayed in the screen
  13. Tap Asset
  14. Check if the visible asset is displayed correctly.
  15. Go back to Account Tab and create a new bitcoin account on testnet via ... on top right in navigation bar
  16. Choose Bitcoin Testnet and input an account name, then tap Add
  17. Observe a new Bitcoin account on testnet has been created and displayed
  18. repeat step 11 to 14 to make sure account and visible asset are being displayed correctly.

Test Bitcoin Account Update

  1. Either tap ... on Bitcoin Account Card then choose Edit in the context menu or go to account details. Tap ... on top right in navigation bar and choose Edit in the context menu
  2. It will open a new screen and only account name can be edited
  3. Edit the name and tap Done
  4. Observe the account name has been updated through out the wallet.

Test BTC or Bitcoin Account Deposit
Flow is the same

  1. Check deposit address and its QR code are being displayed correctly.
  2. Use this deposit address and faucet some BTC, after faucet is finished. This address should get updated to another one.

Check there is no import or export available for bitcoin accounts.

@nuo-xu nuo-xu added CI/skip-android Do not run CI builds for Android CI/skip-macos-x64 Do not run CI builds for macOS x64 CI/skip-windows-x64 Do not run CI builds for Windows x64 labels Mar 22, 2024
@nuo-xu nuo-xu self-assigned this Mar 22, 2024
@nuo-xu nuo-xu marked this pull request as ready for review March 22, 2024 20:38
@nuo-xu nuo-xu requested a review from a team as a code owner March 22, 2024 20:38
@@ -253,6 +253,7 @@ private struct SiteConnectionDetailView: View {
ForEach(siteConnection.connectedAddresses, id: \.self) { address in
AccountView(
address: address,
seed: address, // TODO: Should be the same for blockie seed and the address in dapps for all networks including bitcoin. Bitcoin dapp site permission, we should store the AccountInfo.AccountId.UniqueKey.sha256. Might need to update the UI if the permission in bitcoin dapp will be handled differently.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This TODO is very early, no Bitcoin DApp support yet, but it's good we checked early 😄! iOS TODO's should have issue linked. I think either open iOS specific issue that links to brave/brave-browser#34966 & include iOS issue # in this TODO comment, or drop this comment.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we somehow assert this view is eth sol only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@supermassive i dont think we need an assertion here since we only fetch permission status for .dapps

for coin in WalletConstants.supportedCoinTypes(.dapps) {

And supported coin type is only for .eth and .sol which is stored here

bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo]
) -> QRCodeViewModel {
if let bitcoinAccount = bitcoinAccounts[account.accountId.uniqueKey] {
return .init(name: account.name, address: bitcoinAccount.nextReceiveAddress.addressString)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, when next_receive_address updates, does AccountsChanged fire? Uncommon case, but I think this address could change with Deposit screen open for Bitcoin account, if so then we likely want to update this address & QR Code on Deposit. Might be issue on desktop too cc @supermassive

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did we ever resolve this?

Copy link
Collaborator

@StephenHeaps StephenHeaps Jun 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't believe so. Desktop polls the receive address every 10 minutes, though I'm not sure if there is a better way (IIRC we only monitor blockchain when there is a pending tx from our wallet). @supermassive any thoughts?

isPresentingExportAccount = true
} label: {
Label(Strings.Wallet.exportButtonTitle, braveSystemImage: "leo.key")
if store.account.coin != .btc {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe .zec here also?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright, I think now that handling also .zec is not what we need

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, you are right.

.listRowBackground(Color(.secondaryBraveGroupedBackground))
}
accountNameSection
if isJSONImported {
originPasswordSection
}
privateKeySection
if selectedCoin != .btc && preSelectedCoin != .btc {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.zec

@@ -180,7 +180,7 @@ struct PortfolioAssetGroupHeaderView: View {
if case .network(let networkInfo) = group.groupType {
NetworkIconView(network: networkInfo, length: 32)
} else if case .account(let accountInfo) = group.groupType {
Blockie(address: accountInfo.address)
Blockie(address: accountInfo.blockieSeed)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have some Blockie factory which is able to create an instance from just accountInfo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its fine to keep it only taking the String value since we are not only using it for AccountInfo but also for string type address from TxInfo, dapps' requests and even for custom network's icon based on network name.
blockieSeed is added as en extension computed variable in AccountInfo which can easily check what is the correct seed value for this AccountInfo

@@ -162,15 +162,15 @@ struct EditSiteConnectionView: View {
if sizeCategory.isAccessibilityCategory {
VStack {
AddressView(address: account.address) {
AccountView(address: account.address, name: account.name)
AccountView(address: account.address, seed: account.address, name: account.name)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should AccountView be parametrized by account instead of its fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

adding a new constructor that only takes an AccountInfo. the old constructer is still needed for places that don't have AccountInfo for example SiteConnectionDetailView

Comment on lines 93 to 103
private static func sameBalanceSort(lhs: AssetGroupViewModel, rhs: AssetGroupViewModel) -> Bool {
if case .account(let lhsAccount) = lhs.groupType, case .account(let rhsAccount) = rhs.groupType
{
if lhsAccount.coin == .fil && rhsAccount.coin == .fil {
if lhsAccount.keyringId == .filecoin && rhsAccount.keyringId != .filecoin {
return true
} else if lhsAccount.keyringId != .filecoin && rhsAccount.keyringId == .filecoin {
return false
}
} else if lhsAccount.coin == .btc && rhsAccount.coin == .btc {
if lhsAccount.keyringId == .bitcoin84 && rhsAccount.keyringId != .bitcoin84 {
return true
} else if lhsAccount.keyringId != .bitcoin84 && rhsAccount.keyringId == .bitcoin84 {
return false
}
} else {
if lhsAccount.keyringId == .solana && rhsAccount.keyringId != .solana {
return true
} else if lhsAccount.keyringId != .solana && rhsAccount.keyringId == .solana {
return false
}
}
}
if case .network(let lhsNetwork) = lhs.groupType, case .network(let rhsNetwork) = rhs.groupType
{
let isLHSPrimaryNetwork = WalletConstants.primaryNetworkChainIds.contains(lhsNetwork.chainId)
let isRHSPrimaryNetwork = WalletConstants.primaryNetworkChainIds.contains(rhsNetwork.chainId)
if isLHSPrimaryNetwork && !isRHSPrimaryNetwork {
return true
} else if !isLHSPrimaryNetwork && isRHSPrimaryNetwork {
return false
} else if isLHSPrimaryNetwork, isRHSPrimaryNetwork,
lhsNetwork.chainId != rhsNetwork.chainId,
lhsNetwork.chainId == BraveWallet.SolanaMainnet
{
// Solana Mainnet to be first primary network
return true
} else if isLHSPrimaryNetwork, isRHSPrimaryNetwork,
lhsNetwork.chainId != rhsNetwork.chainId,
rhsNetwork.chainId == BraveWallet.SolanaMainnet
{
// Solana Mainnet to be first primary network
return false
}
return lhs.id < rhs.id
}
return lhs.id < rhs.id
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to sort this as an array of tuples?
https://holyswift.app/using-tuples-to-complex-sorting-operations-in-swift/

let sortedMovieList = movieList.sorted {
    ($0.title, $0.year, $0.director) < ($1.title, $1.year, $1.director)
}

for movie in sortedMovieList {
    print(movie)
}

Copy link
Contributor Author

@nuo-xu nuo-xu Mar 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this requires each property inside the tuple conforms Comparable protocol. In your example, they are either String or Int, they are all Comparable using the default < implementation. In our case, we have more complex logic of comparison of lhs and rhs object rather than the default < implementation.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It doesn't feel right if swift's sugar doesn't support that, neverthelss if tuple sort could not be applied here then at least this algorithm needs a unit test.

Copy link
Collaborator

@supermassive supermassive left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

btc related lgtm

Copy link
Collaborator

@StephenHeaps StephenHeaps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Just a couple issues with Buy, and one issue with the wallet panel showing loading state when Bitcoin Account is currently selected, I believe caused by address check:

https://github.com/brave/brave-core/blob/master/ios/brave-ios/Sources/BraveWallet/Panels/WalletPanelView.swift#L48-L51

@nuo-xu nuo-xu force-pushed the wallet/ios-bitcoin-account-creation branch from bd8465e to 4a3a205 Compare March 28, 2024 19:19
Copy link
Contributor

[puLL-Merge] - brave/brave-core@22737

Description

This PR adds support for Bitcoin in the Brave Wallet iOS app. It allows users to create Bitcoin accounts, view balances, and deposit BTC.

Changes

Changes

  • BVC+Wallet.swift: Added bitcoinWalletService to WalletStore and CryptoStore initializers
  • AddressView.swift: Only show copy address button if address is not empty
  • Assets: Added new "bitcoin-asset-icon" image asset
  • AccountSelectionRootView.swift, AccountDetailsView.swift, AccountView.swift, AccountActivityView.swift, AccountsView.swift: Use account.blockieSeed instead of account.address for Blockie image, check for empty address before displaying
  • AddAccountView.swift: Pass selected Bitcoin network when adding account, don't show private key field for BTC
  • AssetDetailView.swift, BuyTokenStore.swift, DepositTokenStore.swift, DepositTokenView.swift: Support Bitcoin accounts
  • Extensions: Added extensions for BitcoinWalletService, new BraveWalletExtensions for Bitcoin, QR code and SHA256 hash helpers
  • Views: Updated various wallet views to handle Bitcoin accounts and empty addresses
  • Constants & Strings: Added Bitcoin constants and localized strings
  • Tests: Updated unit tests for Bitcoin support

Security Hotspots

  • Carefully review usage of BitcoinWalletService to ensure addresses and account info are handled securely
  • Ensure empty Bitcoin addresses are gracefully handled throughout the UI to avoid unexpected crashes
  • Double check that toggling Bitcoin feature flags enables/disables all Bitcoin functionality as expected

@nuo-xu
Copy link
Contributor Author

nuo-xu commented Apr 2, 2024

There is an know issue that is caused by brave/brave-browser#37192
the selected network is not updated correctly if user switching between bitcoin mainnet account and bitcoin testnet account.
As discussed with @jamesmudgett @supermassive @StephenHeaps we will drop bitcoin testnet for production for now. And it will be done in a separate PR (issue brave/brave-browser#37256)

Copy link
Collaborator

@StephenHeaps StephenHeaps left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just the known issue with switching from Bitcoin Mainnet account <-> Bitcoin Testnet account not updating the network to Bitcoin Mainnet / Testnet respectively. Bitcoin remains behind feature flag so the fix will be handled in separate PR with brave/brave-browser#37256.

LGTM 🐎.

private var keyringServiceObserver: KeyringServiceObserver?
private var walletServiceObserver: WalletServiceObserver?

@Published var prefilledToken: BraveWallet.BlockchainToken?
var prefilledAccount: BraveWallet.AccountInfo?
@Published var allAccounts: [BraveWallet.AccountInfo] = []
@Published var allNetworks: [BraveWallet.NetworkInfo] = []
@Published var bitcoinAccounts: [String: BraveWallet.BitcoinAccountInfo] = [:]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: comment for what the dictionary key is

@nuo-xu nuo-xu merged commit 80b3548 into master Apr 2, 2024
19 checks passed
@nuo-xu nuo-xu deleted the wallet/ios-bitcoin-account-creation branch April 2, 2024 17:57
@mihaiplesa mihaiplesa added this to the 1.66.x - Nightly milestone Apr 2, 2024
contractAddress: "",
name: "Bitcoin",
logo: "",
isErc20: false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: we should probably be making BlockchainToken an interface specific to the coin type rather than making these overlap between all of the networks

]
let mockBtcBalanceInWei =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: this also looks like a leaky abstraction here

Copy link
Member

@kdenhartog kdenhartog left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

few nits and one question, but nothing I see is blocking.

Leaving a post merge sec review approval on this PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CI/skip-android Do not run CI builds for Android CI/skip-macos-x64 Do not run CI builds for macOS x64 CI/skip-windows-x64 Do not run CI builds for Windows x64 puLL-Merge
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support Creating Bitcoin Accounts on iOS
5 participants