bitshares研究系列【bitshares-ui transfer流程】

chaimyu(58)
Published in
#cn
Words
261
Reading
2 min
9y
Listen to this post
~2 min

bitshares-ui transfer流程

1. 界面显示转帐参数,点击转帐按钮转帐

components/Transfer/Transfer.jsx

    onSubmit(e) {
        ...
        AccountActions.transfer(
            this.state.from_account.get("id"),
            this.state.to_account.get("id"),
            sendAmount.getAmount(),
            asset.get("id"),
            this.state.memo
                ? new Buffer(this.state.memo, "utf-8")
                : this.state.memo,
            this.state.propose ? this.state.propose_account : null,
            this.state.feeAsset ? this.state.feeAsset.get("id") : "1.3.0"
        )
            .then(() => {
                this.resetForm.call(this);
                TransactionConfirmStore.unlisten(this.onTrxIncluded);
                TransactionConfirmStore.listen(this.onTrxIncluded);
            })
            .catch(e => {
                let msg = e.message
                    ? e.message.split("\n")[1] || e.message
                    : null;
                console.log("error: ", e, msg);
                this.setState({error: msg});
            });
    }

onSubmit会调用actions中的AccountActions.transfer

2. AccountActions.transfer

app/actions/AccountActions.js

    transfer(
        from_account,
        to_account,
        amount,
        asset,
        memo,
        propose_account = null,
        fee_asset_id = "1.3.0"
    ) {
        // Set the fee asset to use
        fee_asset_id = accountUtils.getFinalFeeAsset(
            propose_account || from_account,
            "transfer",
            fee_asset_id
        );

        try {
            return dispatch => {
                return ApplicationApi.transfer({
                    from_account,
                    to_account,
                    amount,
                    asset,
                    memo,
                    propose_account,
                    fee_asset_id
                }).then(result => {
                    // console.log( "transfer result: ", result )

                    dispatch(result);
                });
            };
        } catch (error) {
            console.log(
                "[AccountActions.js:90] ----- transfer error ----->",
                error
            );
            return new Promise((resolve, reject) => {
                reject(error);
            });
        }
    }

actions只是封装一下简单调用ApplicationApi.transfer

3. ApplicationApi.transfer

app/api/ApplicationApi.js

转帐的主要实现在这个函数中,函数前面会对取帐号资产数据,并对memo加密等,真正的交易执行在以下代码:

                let tr = new TransactionBuilder();
                let transfer_op = tr.get_type_operation("transfer", {
                    fee: {
                        amount: 0,
                        asset_id: fee_asset_id
                    },
                    from: chain_from.get("id"),
                    to: chain_to.get("id"),
                    amount: {amount, asset_id: chain_asset.get("id")},
                    memo: memo_object
                }); 

                return tr.update_head_block().then(() => {
                    if (propose_account) {
                        tr.add_type_operation("proposal_create", {
                            proposed_ops: [{op: transfer_op}],
                            fee_paying_account: propose_acount_id
                        });
                    } else {
                        tr.add_operation(transfer_op);
                    }

                    return WalletDb.process_transaction(
                        tr,
                        null, //signer_private_keys,
                        broadcast
                    );
                });
            })

生成TransactionBuilder对象,通过get_type_operation取得传输数据,并add_operation加到operation列表中,最后调用WalletDb.process_transaction

4. WalletDb.process_transaction

app/stores/WalletDB.js

    process_transaction(tr, signer_pubkeys, broadcast, extra_keys = []) {
        ... 签名处理
                                .then(() => {
                            if (broadcast) {
                                if (this.confirm_transactions) {
                                    let p = new Promise((resolve, reject) => {
                                        TransactionConfirmActions.confirm(
                                            tr,
                                            resolve,
                                            reject
                                        );
                                    });
                                    return p;
                                } else return tr.broadcast();
                            } else return tr.serialize();
                        });

调用到TransactionBuilder的broadcast

5. TransactionBuilder.broadcast

bitsharesjs/dist/chain/src/TransactionBuilder.js

function _broadcast(was_broadcast_callback) {
    var _this5 = this;

    return new Promise(function (resolve, reject) {
        ...
        var tr_object = ops.signed_transaction.toObject(_this5);
        // console.log('... broadcast_transaction_with_callback !!!')
        Apis.instance().network_api().exec("broadcast_transaction_with_callback", [function (res) {
            return resolve(res);
        }, tr_object]).then(function () {
            //console.log('... broadcast success, waiting for callback')
            if (was_broadcast_callback) was_broadcast_callback();
            return;
        }).catch(function (error) {
            // console.log may be redundant for network errors, other errors could occur
            console.log(error);
            var message = error.message;
            if (!message) {
                message = "";
            }
            reject(new Error(message + "\n" + 'bitshares-crypto ' + ' digest ' + hash.sha256(_this5.tr_buffer).toString('hex') + ' transaction ' + _this5.tr_buffer.toString('hex') + ' ' + JSON.stringify(tr_object)));
            return;
        });
        return;
    });
}

在broadcast_transaction_with_callback执行指定了回调函数,交易的结果会在回调函数中返回。

6. GrapheneApi.exec / call

    GrapheneApi.prototype.exec = function exec(method, params) {
        return this.ws_rpc.call([this.api_id, method, params]).catch(function (error) {
            console.log("!!! GrapheneApi error: ", method, params, error, JSON.stringify(error));
            throw error;
        });
    };

7. ChainWebSocket.call

    ChainWebSocket.prototype.call = function call(params) {
        var _this2 = this;

        if (this.ws.readyState !== 1) {
            return Promise.reject(new Error('websocket state error:' + this.ws.readyState));
        }
        var method = params[1];
        if (SOCKET_DEBUG) console.log("[ChainWebSocket] >---- call ----->  \"id\":" + (this.cbId + 1), JSON.stringify(params));

        this.cbId += 1;

        if (method === "set_subscribe_callback" || method === "subscribe_to_market" || method === "broadcast_transaction_with_callback" || method === "set_pending_transaction_callback") {
            // Store callback in subs map
            this.subs[this.cbId] = {
                callback: params[2][0]
            };

            // Replace callback with the callback id
            params[2][0] = this.cbId;
        }

        if (method === "unsubscribe_from_market" || method === "unsubscribe_from_accounts") {
            if (typeof params[2][0] !== "function") {
                throw new Error("First parameter of unsub must be the original callback");
            }

            var unSubCb = params[2].splice(0, 1)[0];

            // Find the corresponding subscription
            for (var id in this.subs) {
                if (this.subs[id].callback === unSubCb) {
                    this.unsub[this.cbId] = id;
                    break;
                }
            }
        }

        var request = {
            method: "call",
            params: params
        };
        request.id = this.cbId;
        this.send_life = max_send_life;

        return new Promise(function (resolve, reject) {
            _this2.cbs[_this2.cbId] = {
                time: new Date(),
                resolve: resolve,
                reject: reject
            };
            _this2.ws.send(JSON.stringify(request));
        });
    };

以上代码对"broadcast_transaction_with_callback"做了判断,并替换回调处理。

8. ChainWebSocket.listener

    ChainWebSocket.prototype.listener = function listener(response) {
        if (SOCKET_DEBUG) console.log("[ChainWebSocket] <---- reply ----<", JSON.stringify(response));

        var sub = false,
            callback = null;

        if (response.method === "notice") {
            sub = true;
            response.id = response.params[0];
        }

        if (!sub) {
            callback = this.cbs[response.id];
            this.responseCbId = response.id;
        } else {
            callback = this.subs[response.id].callback;
        }

        if (callback && !sub) {
            if (response.error) {
                callback.reject(response.error);
            } else {
                callback.resolve(response.result);
            }
            delete this.cbs[response.id];

            if (this.unsub[response.id]) {
                delete this.subs[this.unsub[response.id]];
                delete this.unsub[response.id];
            }
        } else if (callback && sub) {
            callback(response.params[1]);
        } else {
            console.log("Warning: unknown websocket response: ", response);
        }
    };

transfer不是"notice"消息,会根据response.id找到回调函数,把结果消息callback后,从回调列表中删除。

注意这个函数中一个是cbs数组,一个是subs数组


感谢您阅读 chaimyu@chaimyu 的帖子,期待您能留言交流!

bitshares研究系列【bitshares-ui transfer流程】 | Ecency