您正在查看: EOS-新手教程 分类下的文章

Scatter 身份获取部分代码

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打開調試終端,開始調試。

EOS time_point_sec 时间计算

EOS专门对时间计算进行了封装,在eosiolib/time.hpp中提供了多种关于时间计算的封装,简单介绍一下time_point_sec的用法

auto time_now = time_point_sec(now()/*获取当前区块时间*/);
uint32_t five_minutes = 5 * 60;
five_minutes_later = time_now + five_minutes;
five_minutes_later > time_now;

得到的time_point_sec类型可以与uint32_t类型进行简单的加/减数值运算,同类型间也可以直接用大于/小于等比较符进行运算,满足大多数时间运算需求了已经。
转载自:github

创建默认的genesis.json

创建genesis.json文件

 nodeos --extract-genesis-json genesis.json

打印genesis-json

--print-genesis-json

获取RAM实时价格

命令如下

cleos -u https://nodes.get-scatter.com:443 get table eosio eosio rammarket

获取的结果为

{
  "rows": [{
      "supply": "10000000000.0000 RAMCORE",
      "base": {
        "balance": "12341975994 RAM",
        "weight": "0.50000000000000000"
      },
      "quote": {
        "balance": "5568015.6361 EOS",
        "weight": "0.50000000000000000"
      }
    }
  ],
  "more": false
}

然后使用Bancor算法,计算出需要nKB的RAM的价格

RAM价格 = (n * quote.balance) / (n + base.balance / 1024)

RPC代码实现如下

const wif = '5JC9FdRjX3c3fsB62S5Tz22LgTuRHegb1XEV16uft8u3njmm9E5'
eos = Eos({
    httpEndpoint: 'https://nodes.get-scatter.com:443',
    chainId: 'aca376f206b8fc25a6ed44dbdc66547c36c6c33e3a119ffbeaef943642f0e906',
    keyProvider: wif,
    verbose: true
    })
eos.getTableRows(true,"eosio", "eosio", "rammarket").then(result => {
    console.log(1 * result.rows[0].quote.balance.split(" ")[0] / (1 + result.rows[0].base.balance.split(" ")[0] / 1024));
}).catch(e => {
    console.error(e);
})

当前返回价格:0.4716754832469308 EOS/Kib

参考:github

EOS发起交易时,提示ram,net或cpu资源不足问题分析与解决(Error 3080001: account using more than allotted RAM usage)

一般创建账号,或者三方购买的账号都是最低的资源创建的 (cleos system newaccount 消耗最少的EOS资源
基本账号下CPU和NET都是零,RAM 3kb以下,因为RAM收费逻辑(EOS零手续费免费?你不知道的EOS收费细节

  • 只要action中执行了持久化存储相关的逻辑就需要收取ram使用费,比如系统合约的newaccount ,updateauth, setcode, setabi, schedule_deferred_transaction, eosio.token的transfer

所以在账号资源不足的情况下,转账操作会提示以下错误

surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos push action eosio.token transfer '["eosio", "bcskillsurou","100.0000 EOS","vote"]' -p eosio
executed transaction: e233ccc335b833ac9d7746a43e3b4e591795151c95f989a36976f8ceb98a08c7  136 bytes  562 us
#   eosio.token <= eosio.token::transfer        {"from":"eosio","to":"bcskillsurou","quantity":"100.0000 EOS","memo":"vote"}
#         eosio <= eosio.token::transfer        {"from":"eosio","to":"bcskillsurou","quantity":"100.0000 EOS","memo":"vote"}
#  bcskillsurou <= eosio.token::transfer        {"from":"eosio","to":"bcskillsurou","quantity":"100.0000 EOS","memo":"vote"}
warning: transaction executed locally, but may not be confirmed by the network yet
surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos get currency balance eosio.token bcskillsurou
100.0000 EOS
surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos push action eosio.token transfer '["bcskillsurou", "eosio","1.0000 EOS","vote"]' -p bcskillsurou
Error 3080001: account using more than allotted RAM usage

提示RAM不足已支持转账
如果自己给自己直接购买RAM的话

surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos system buyram bcskillsurou bcskillsurou "0.1000 EOS"
3536793ms thread-0   main.cpp:429                  create_action        ] result: {"binargs":"a0e9d5384607313aa0e9d5384607313ae80300000000000004454f5300000000"} arg: {"code":"eosio","action":"buyram","args":{"payer":"bcskillsurou","receiver":"bcskillsurou","quant":"0.1000 EOS"}} 
Error 3080002: transaction exceeded the current network usage limit imposed on the transaction

提示NET不足
如果自己给自己抵押换取NET和CPU时

surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos system delegatebw bcskillsurou bcskillsurou '0.1000 EOS' '0.1000 EOS' -p bcskillsurou
48402ms thread-0   main.cpp:1084                 operator()           ] act_payload: {"from":"bcskillsurou","receiver":"bcskillsurou","stake_net_quantity":"0.1000 EOS","stake_cpu_quantity":"0.1000 EOS","transfer":false} 
48403ms thread-0   main.cpp:429                  create_action        ] result: {"binargs":"a0e9d5384607313aa0e9d5384607313ae80300000000000004454f5300000000e80300000000000004454f530000000000"} arg: {"code":"eosio","action":"delegatebw","args":{"from":"bcskillsurou","receiver":"bcskillsurou","stake_net_quantity":"0.1000 EOS","stake_cpu_quantity":"0.1000 EOS","transfer":false}} 
Error 3080001: account using more than allotted RAM usage

又提示RAM不足...先有鸡还是先有蛋,死循环。所以只能让其他RAM资源充足的账号例如 eosio 帮此账号bcskillsurou购买

surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos system buyram eosio bcskillsurou "0.1000 EOS"
177473ms thread-0   main.cpp:429                  create_action        ] result: {"binargs":"0000000000ea3055a0e9d5384607313ae80300000000000004454f5300000000"} arg: {"code":"eosio","action":"buyram","args":{"payer":"eosio","receiver":"bcskillsurou","quant":"0.1000 EOS"}} 
executed transaction: f5aa4dd7f6a850b65884174a4f86285414ca5c50ba1ab6ddc2a7f9ba5b65c051  128 bytes  1814 us
#         eosio <= eosio::buyram                {"payer":"eosio","receiver":"bcskillsurou","quant":"0.1000 EOS"}
#   eosio.token <= eosio.token::transfer        {"from":"eosio","to":"eosio.ram","quantity":"0.0995 EOS","memo":"buy ram"}
#         eosio <= eosio.token::transfer        {"from":"eosio","to":"eosio.ram","quantity":"0.0995 EOS","memo":"buy ram"}
#     eosio.ram <= eosio.token::transfer        {"from":"eosio","to":"eosio.ram","quantity":"0.0995 EOS","memo":"buy ram"}
#   eosio.token <= eosio.token::transfer        {"from":"eosio","to":"eosio.ramfee","quantity":"0.0005 EOS","memo":"ram fee"}
#         eosio <= eosio.token::transfer        {"from":"eosio","to":"eosio.ramfee","quantity":"0.0005 EOS","memo":"ram fee"}
#  eosio.ramfee <= eosio.token::transfer        {"from":"eosio","to":"eosio.ramfee","quantity":"0.0005 EOS","memo":"ram fee"}
warning: transaction executed locally, but may not be confirmed by the network yet

bcskillsurouRAM足够后,就可以自己或者其他账号抵押购买CPU和NET了

surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos system delegatebw bcskillsurou bcskillsurou '0.1000 EOS' '0.1000 EOS' -p bcskillsurou
214840ms thread-0   main.cpp:1084                 operator()           ] act_payload: {"from":"bcskillsurou","receiver":"bcskillsurou","stake_net_quantity":"0.1000 EOS","stake_cpu_quantity":"0.1000 EOS","transfer":false} 
214841ms thread-0   main.cpp:429                  create_action        ] result: {"binargs":"a0e9d5384607313aa0e9d5384607313ae80300000000000004454f5300000000e80300000000000004454f530000000000"} arg: {"code":"eosio","action":"delegatebw","args":{"from":"bcskillsurou","receiver":"bcskillsurou","stake_net_quantity":"0.1000 EOS","stake_cpu_quantity":"0.1000 EOS","transfer":false}} 
executed transaction: b57bad80a397c6fee9aab0519600b9c063f6084fadd542402fe07ce93d2643bb  144 bytes  1720 us
#         eosio <= eosio::delegatebw            {"from":"bcskillsurou","receiver":"bcskillsurou","stake_net_quantity":"0.1000 EOS","stake_cpu_quanti...
#   eosio.token <= eosio.token::transfer        {"from":"bcskillsurou","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
#  bcskillsurou <= eosio.token::transfer        {"from":"bcskillsurou","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
#   eosio.stake <= eosio.token::transfer        {"from":"bcskillsurou","to":"eosio.stake","quantity":"0.2000 EOS","memo":"stake bandwidth"}
warning: transaction executed locally, but may not be confirmed by the network yet

此时再次转账的话

surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos get currency balance eosio.token bcskillsurou
99.8000 EOS
surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos push action eosio.token transfer '["bcskillsurou", "eosio","1.0000 EOS","vote"]' -p bcskillsurou
executed transaction: cd9a0780b51db71a0efd096578dc453d26a528af6fa4e4e7ab0b139e9d9753ae  136 bytes  548 us
#   eosio.token <= eosio.token::transfer        {"from":"bcskillsurou","to":"eosio","quantity":"1.0000 EOS","memo":"vote"}
#  bcskillsurou <= eosio.token::transfer        {"from":"bcskillsurou","to":"eosio","quantity":"1.0000 EOS","memo":"vote"}
#         eosio <= eosio.token::transfer        {"from":"bcskillsurou","to":"eosio","quantity":"1.0000 EOS","memo":"vote"}
warning: transaction executed locally, but may not be confirmed by the network yet
surou@surou-C-H110M-K-Pro:~/.local/share/eosio/nodeos/config$ cleos get currency balance eosio.token bcskillsurou
98.8000 EOS

就一切OK了