stacks-archive / blockstack-browser

The Blockstack Browser
Mozilla Public License 2.0
1.12k stars 199 forks source link

Stale profile.json (data loss) #1908

Open zone117x opened 5 years ago

zone117x commented 5 years ago

Related to https://github.com/blockstack/blockstack-browser/issues/1745 @timstackblock and I decided to open a new issue for this since the bug(s) and solution(s) may be a superset of the original issue.


Several actions performed within a blockstack-browser tab cause a cached outdated profile.json to overwrite any changes made from another tab.

Reproducible Example

  1. Keep one or more blockstack-browser tabs open (likely common user behavior).
  2. Sign into a new blockstack app that uses the publish_data scope - look up your profile.json to see the app added. e.g.: https://core.blockstack.org/v1/names/zone117x.id -> https://gaia.blockstack.org/hub/1Nw25PemCRv24UQAcZdaj4uD11nkTCWRTE/profile.json
  3. Go to an open tab and update something like your name, bio, a social proof, etc.
  4. Look up your profile.json again, see the app entry missing.

Potential Fix

Adherence to the single source of truth principle.

Demo code example

async function fetchProfile(identityAddress: string) {
/**
 * First tries:
 *   1. Fetch `names` for the `address` with `bitcoinAddressLookupUrl`. 
 *   2. Fetch zonefile with `nameLookupUrl` using the first name returned. 
 *   3. Parse zonefile for the `profile.json` url. 
 * 
 * In some situations one or more parts of this resolution chain will be 
 * unavailable. Examples: 
 *   * Pending name registration (address but no name/zonefile available).
 *   * Legacy zonefile not containing a `profile.json` url. 
 *   * Legacy `profile.json` file not containing a gaiaHubUrl.
 * 
 * In these situations, the locally cached identities will be checked for a 
 * matching address which may contain the profile.json url if a name 
 * is pending registration. 
 * 
 * If still not found then the default gaiahub server is searched for a matching
 * profile.json file at expected & legacy paths. The existing 
 * `fetchProfileLocations` function can be used for this.
 */
}

async function updateLatestProfile(updateCallback: (profile) => void) {
  const profile = await fetchProfile()
  updateCallback(profile)
  await uploadProfile(profile)
}

async function updateProfileBio(desc: string) {
  await updateLatestProfile(profile => {
    profile.account.description = desc
  })
}

async function updateAvatar(picUrl: string) {
  await updateLatestProfile(profile => {
    let imageObj = profile.image.find(img => img.name === 'avatar')
    imageObj = Object.assign({}, DEFAULT_AVATAR, imageObj)
    imageObj.contentUrl = picUrl
  })
}

async function updateAppBucket(appDomain: string, appBucketUrl: string) {
  await updateLatestProfile(profile => {
    profile.apps[appDomain] = appBucketUrl
  })
}

Stale UI in a tab

(This may need to be moved to a new issue)

Typical user behavior could involve having the blockstack-browser webpage open in a perma-tab. Or several tabs, in potentially different web browsers / devices.

The Page Visibility API https://developer.mozilla.org/en-US/docs/Web/API/Page_Visibility_API could be used to prevent a browser tab state from becoming stale.

A simple document.onvisibilitychange hook can be used to fetch possibly updated profile data, without having to perform something like constant polling.

markmhendrickson commented 5 years ago

@timstackblock Would you consider this bug P2 or P3?

markmhendrickson commented 4 years ago

@yknl I see you added this to a DX sprint a few months ago – was progress made?

@hstove are these fixes we can make implicitly as part of the rebuild?

yknl commented 4 years ago

The latest update to blockstack.js and Gaia that improves concurrency may help with this issue, but some browser changes are required. https://github.com/blockstack/blockstack.js/pull/743