blockstack-browser
blockstack-browser copied to clipboard
Discrepancy between gaiahub in zonefile vs gaiahub in profile.json
A name's zonefile has an implicit pointer to a gaiahub server via the profile.json location.
Example zonefile in https://core.blockstack.org/v1/names/mattlittle.id:
$ORIGIN mattlittle.id
$TTL 3600
_http._tcp IN URI 10 1
"https://gaia.blockstack.org/hub/1Nw25PemCRv24UQAcZdaj4uD11nkTCWRTE/profile.json"
That profile.json is then fetched, and the gaiaHubUrl field is used to connectToGaiaHub, which roughly does the following:
async function connectToGaiaHub(gaiaHubUrl: string, privateKey: string) {
const response = await fetch(`${gaiaHubUrl}/hub_info`)
const hubInfo = await response.json()
return {
url_prefix: hubInfo.read_url_prefix,
address: privateKeyToAddress(privateKey),
token: makeGaiaAuthToken(hubInfo, privateKey, gaiaHubUrl),
server: gaiaHubUrl
}
}
The profile.json write endpoint derived from connection handshake must either resolve to the same url, or at least resolve with a gaia auth token that allows writes to the url in the zonefile. When neither of those assertions are true, it leads to data loss or perception of data loss.
This scenario happens when attempting to change gaiahub servers.
The gaiaHubUrl field in a profile.json can be changed without also performing a name transaction to update the profile.json url within the zonefile. Or, even if both changes are made, this will still cause problems during the ~4 hour pending transaction period.
Example failure scenario
- A name's zonefile and profile.json points to
a-hub.com, which I want to change tob-hub.com:
$ORIGIN matt.id
"https:/a-hub.com/1Nw25PemCRv24UQAcZdaj4uD11nkTCWRTE/profile.json"
- Upload updated
profile.jsonwithgaiaHubUrlchanged froma-hub.comtob-hub.com. - Now the blockstack-browser will perform updates/writes to the
profile.jsonlocated on the newb-hub.comserver, because the new gaia hub auth token will not work for writes to the olda-hub.comserver that the zonefile url specifies. - For some duration, updates to the new profile.json file can be made. This could be several hours if a name update tx was made, or perhaps a few months if the update tx was not made.
- These changes will appear wiped out after an identity refresh or account restoration, since the old profile.json from the zonefile will be used.
Solution Proposals
~~Prevent Gaia writes during this hub server discrepancy~~
Status: will not work in many situations since the discrepancy is not possible reliably detect. No pending zonefile update API available, nothing like a mempool in atlas exists.
The blockstack-browser would have to implement a brittle "pending zonefile update" state cache. This state cache would be inaccessible from an account signed into other machines / device / web-browser, as well as multi-player apps.
Preserving resolution chain with authoritativeProfileURL profile.json field
(from @jcnelson, thanks Jude!)
Add a new field in the profile's api structure called authoritativeProfileURL. This key maps to the URL to the profile on the user's new Gaia hub. If blockstack.js sees the authoritativeProfileURL field in a profile it resolves, then it further resolves and authenticates the profile pointed to by authoritativeProfileURL and returns that instead. The rationale for this change is that it allows the user to change where their profile and app data are hosted without having to send a transaction. To prevent client DDoS's, we would limit the number of such redirections to e.g. 5 before having blockstack.js throw an error (akin to a "too many redirects" HTTP error).
This is something I spoke with @markmhx about as well. My high-level concern is that the user may choose to switch Gaia hubs after they register their Blockstack ID, and when that happens, they'll want both their app data and profile migrated over and made usable ASAP.
The trouble is, migrating to a new Gaia hub requires updating the name-to-profile resolution path so that the profile in the new Gaia hub gets resolved. Historically, this was achieved by issuing a NAME_UPDATE transaction to change the name's pointer to the profile. However, this has an undesirable user experience -- not only is it slow, but also it costs money.
We could update the transaction broadcaster to try and handle profile resolution while the NAME_UPDATE is processing, but I find this unappealing because it leaves other uncomfortable questions unresolved -- for example, who pays the bills for keeping this thing running? What happens if the transaction broadcaster crashes? How do clients know to contact the transaction broadcaster instead of the core node? How do clients know which transaction broadcaster to contact if there are more than one?
My alternative proposal is simply to make it so a profile can resolve to another profile, so long as both profiles are signed by the same key. Then, the old profile simply becomes a pointer to the new profile (its other contents are ignored), and the user has a free and instantaneous way to switch their Gaia hub. A NAME_UPDATE only becomes necessary if the old profile becomes unavailable for some reason, or is no longer signed by the name's key.
I think @jcnelson's proposal works really well and is easy to implement. @kantai - thoughts?
Yeah -- this proposal seems like it would work pretty well to me.