Dapp MonsterEOS 请求Scatter 获取身份信息
const network = {
protocol: CHAIN_PROTOCOL,
blockchain: 'eos',
host: CHAIN_HOST,
port: CHAIN_PORT,
chainId: CHAIN_ID
}
app.ports.scatterRequestIdentity.subscribe(async () => {
await scatter.suggestNetwork(network)
let requiredFields = {
accounts: [network]
}
scatter.getIdentity(requiredFields).then((identity) => {
const user = {
eosAccount: identity.accounts[0].name,
publicKey: identity.publicKey
}
app.ports.setScatterIdentity.send(user)
}).catch(error => {
app.ports.scatterRejected.send("Identity or Network was rejected")
console.error(error)
})
})
开始进入Scatter 相关代码
Scatter\src\scatterdapp.js
suggestNetwork(network){
if(!Network.fromJson(network).isValid()) throws('The provided network is invalid.');
return _send(NetworkMessageTypes.REQUEST_ADD_NETWORK, {
network:network
});
}
getIdentity(fields = {}){
return _send(NetworkMessageTypes.GET_OR_REQUEST_IDENTITY, {
network:network,
fields
}).then(async identity => {
this.useIdentity(identity);
return identity;
});
}
Scatter\src\content.js
contentListener(msg){
if(!isReady) return;
if(!msg) return;
if(!stream.synced && (!msg.hasOwnProperty('type') || msg.type !== 'sync')) {
stream.send(nonSyncMessage.error(Error.maliciousEvent()), PairingTags.INJECTED);
return;
}
// Always including the domain for every request.
msg.domain = strippedHost(); //此时domain被赋值
if(msg.hasOwnProperty('payload'))
msg.payload.domain = strippedHost();
let nonSyncMessage = NetworkMessage.fromJson(msg);
switch(msg.type){
case 'sync': this.sync(msg); break;
case NetworkMessageTypes.GET_OR_REQUEST_IDENTITY: this.getOrRequestIdentity(nonSyncMessage); break;
getOrRequestIdentity(message){
if(!isReady) return;
InternalMessage.payload(InternalMessageTypes.GET_OR_REQUEST_IDENTITY, message.payload)
.send().then(res => this.respond(message, res))
}
Scatter\src\background.js
dispenseMessage(sendResponse, message){
Background.checkAutoLock();
switch(message.type){
case InternalMessageTypes.GET_OR_REQUEST_IDENTITY: Background.getOrRequestIdentity(sendResponse, message.payload); break;
Scatter\src\background.js
static getOrRequestIdentity(sendResponse, payload){
this.lockGuard(sendResponse, () => {
Background.load(scatter => {
const {domain, fields} = payload;
IdentityService.getOrRequestIdentity(domain, fields, scatter, (identity, fromPermission) => {
if(!identity){
sendResponse(Error.signatureError("identity_rejected", "User rejected the provision of an Identity"));
return false;
}
if(!fromPermission) {
this.addHistory(HistoricEventTypes.PROVIDED_IDENTITY, {
domain,
provided:!!identity,
identityName:identity ? identity.name : false,
publicKey:(identity) ? identity.publicKey : false
});
this.addPermissions([Permission.fromJson({
domain,
identity:identity.publicKey,
timestamp:+ new Date(),
fields,
checksum:domain
})])
}
sendResponse(identity);
});
});
})
}
Scatter\src\services\IdentityService.js
static identityFromPermissionsOrNull(domain, scatter){
const identityFromPermission = IdentityService.identityPermission(domain, scatter);
return identityFromPermission ? identityFromPermission.getIdentity(scatter.keychain) : null;
}
static getOrRequestIdentity(domain, fields, scatter, callback){
// Possibly getting an Identity that has been synced with this application.
const identityFromPermission = IdentityService.identityFromPermissionsOrNull(domain, scatter);
let identity = identityFromPermission;
const sendBackIdentity = id => {
if(!id || id.hasOwnProperty('isError')){
callback(null, null);
return false;
}
callback(id.asOnlyRequiredFields(fields), !!identityFromPermission);
};
if(identity){
// Even though there is a previous permission,
// the identity might have changed and no longer
// meets the requirements.
if(identity.hasRequiredFields(fields)){
sendBackIdentity(identity);
return false;
} else {
// TODO: Remove permission
}
}
else NotificationService.open(new Prompt(PromptTypes.REQUEST_IDENTITY, domain, null, fields, sendBackIdentity)); //进入此处开始下面逻辑
}
Scatter\src\prompts\RequestIdentityPrompt.vue
methods: {
bind(changed, original) { this[original] = changed },
filteredIdentities(){
return this.identities
.filter(id => id.hasRequiredFields(this.identityFields))
.filter(id => JSON.stringify(id).indexOf(this.searchText) !== -1)
},
formatProp(prop){
if(prop instanceof Network) return `${prop.blockchain.toUpperCase()} Account`;
return prop;
},
formatPropValue(identity, prop){
const value = identity.getPropertyValueByName(prop);
if(prop instanceof Network) return PluginRepository.plugin(prop.blockchain).accountFormatter(value);
else if (prop === 'country') return value.name;
return value;
},
selectIdentity(identity){
this.selectedIdentity = identity;
},
accepted(){
if(!this.selectedIdentity){
this[Actions.PUSH_ALERT](AlertMsg.YouMustSelectAnIdentity());
return false;
}
const identity = this.identities.find(id => id.publicKey === this.selectedIdentity.publicKey);
this.prompt.responder(identity);
NotificationService.close();
},
denied(){
this.prompt.responder(null);
NotificationService.close();
},
...mapActions([
Actions.UPDATE_STORED_SCATTER,
Actions.PUSH_ALERT,
Actions.PUSH_PROMPT
])
}
Scatter\src\models\Identity.js
/***
* Checks if an Identity has specified fields.
* This is used when an interacting application requires specific information.
* @param fields - The fields to check for
* @returns {boolean}
*/
hasRequiredFields(fields){
const requiredFields = IdentityRequiredFields.fromJson(fields);
if(!requiredFields.isValid()) return false;
if(requiredFields.personal.length)
if(!requiredFields.personal.every(field => this.personal[field].length))
return false;
if(requiredFields.location.length)
if(!this.locations.find(location => location.hasFields(requiredFields.location)))
return false;
if(requiredFields.accounts.length)
if(!requiredFields.accounts.every(network => this.hasAccount(network)))
return false;
return true;
}
static placeholder(){ return new Identity(); }
static fromJson(json){
let p = Object.assign(this.placeholder(), json);
if(json.hasOwnProperty('accounts')) p.accounts = Object.keys(json.accounts).reduce((acc, network) => {
acc[network] = Account.fromJson(json.accounts[network]);
return acc;
}, {});
p.personal = PersonalInformation.fromJson(json.personal);
if(json.hasOwnProperty('locations')) p.locations = json.locations.map(location => LocationInformation.fromJson(location));
else p.locations = [LocationInformation.placeholder()];
return p;
}
查找Scatter存储的身份中,选取网络匹配的账号,返回到界面,由用户选中
hasAccount(network){ return this.accounts.hasOwnProperty(network.unique()) }
Scatter\src\models\Network.js
unique(){ return (`${this.blockchain}:` + (this.chainId.length ? `chain:${this.chainId}` : `${this.host}:${this.port}`)).toLowerCase(); }
${this.blockchain}
为公链固定的类型,比如EOS为eos
如果Dapp配置了所需网络的chain Id,则计算返回为("${this.blockchain}:"+"chain:${this.chainId}").toLowerCase()
如果没有配置chain Id,则计算返回 ("${this.blockchain}:"+"${this.host}:${this.port}").toLowerCase()
总结
由于大多Dapp都配置了chain id,所以查找Scatter中相应的身份,及chain id相同网络设置的身份。
添加身份
Scatter\src\background.js
static addPermissions(permissions){
this.load(scatter => {
permissions.map(permission => {
if(!scatter.keychain.hasPermission(permission.checksum, permission.fields))
scatter.keychain.permissions.unshift(permission);
});
this.update(() => {}, scatter);
})
}
附加background.js方法
在chrome打開地址 chrome://extensions/
,點擊background page
打開調試終端,開始調試。