Facebook最新Libra币开发指南---接口服务器开发2

7 篇文章 0 订阅
7 篇文章 0 订阅

在上一篇博文中,我们已经使用Rust语言开发了一款简单的Web服务器,虽然以单线程方式工作,但是可以正确解析Libra Core的常见命令,完成了程序的基本框架,在这一篇文件中,我们将带领大家逐个实现这些命令,最后形成一个基本完整的程序,最后集成Libra Core的client工具中。这样我们就有一个Libra Core的Web接口,大家就可以将自己的应用系统第一时间移植到Libra Core,相信应该在绝对是Libra Core上的第一批应用,为明年Facebook推出的主网商用服务抢占有利的位置。

集成到命令行工具

我们首先需要将我们开发的Web服务器,集成到Libra Core Client中。首先打开libra/client/src/main.rs文件,将上一篇博文中的代码,除main函数外,全部拷贝到libra/client/src/main.rs文件中。
拷贝完成之后,我们首先编译运行一下Libra Core Client,以保证我们的程序没有编译错误,在libra目录下运行如下命令:

./scripts/cli/start_cli_testnet.sh

在编译过程中会有一些警告信息,提示我们定义了一些函数而没有使用这些函数,因为我们定义的命令处理函数,确实没有调用过,所以我们可以暂时忽略这些警告信息。
接着我们在原来loop循环开始时调用启动我们服务器的方法,启动服务器,如下所示:

......
    let config = Config::builder()
        .history_ignore_space(true)
        .completion_type(CompletionType::List)
        .auto_add_history(true)
        .build();
    let mut rl = Editor::<()>::with_config(config);
    start_server(&mut client_proxy);
    /*loop {
        let readline = rl.readline("libra% ");
        match readline {
            Ok(line) => {
            ......
*/

在上面的代码中,我们将client_proxy作为参数传入,实际上所有调用Libra Core功能的操作,均由此对象来实现。
重新编译运行客户端。我们应该可以看到Web服务器已经正常启动,在浏览器上输入http://127.0.0.1:7878/ls?cmd=query_balance&account_id=88 ,会显示如下所示的结果:
在这里插入图片描述
如果可以显示上述结果,就证明我们的Web服务器已经正常启动,并且可以正常接受和处理Libra Core的命令了。下面我们将在接收到客户端发送过来的命令之后,调用Libra Core相应接口,执行相应的逻辑,最后将调用结果返回给客户端。

改造启动方法和处理连接方法

由于我们要在Libra Core Client的模式下运行,我们的启动方法需要做出如下改动:

fn start_server(client_proxy: &mut ClientProxy) {
    println!("Libra Server v0.0.3 Starting up ...");
    let listener = TcpListener::bind("127.0.0.1:7878").unwrap();
    for stream in listener.incoming() {
        let stream = stream.unwrap();
        handle_connection(stream, client_proxy);
    }
}

我们的连接处理方法需要做出如下改动:

fn handle_connection(mut stream: TcpStream, client_proxy: &mut ClientProxy) {
    let mut contents: String = String::from("Hello World!");
    let mut buffer = [0; 1024];
    // 获取请求信息
    stream.read(&mut buffer).unwrap();
    println!("Request: {}", String::from_utf8_lossy(&buffer[..]));
    let request = String::from_utf8_lossy(&buffer[..]);
    // 不处理请求网站图标请求
    if request.find("GET /favicon.ico HTTP/1.1") >= Some(0) {
        return ;
    }
    // 请出请求中的query string
    let query_string = &get_query_string(&request);
    println!("query_string:{}", query_string);
    let cmd = get_cmd_param(query_string.to_string());
    println!("接收到命令:cmd={}!", cmd);
    let params: Vec<_> = query_string.split("&").collect();
    if cmd.find("account_create")>=Some(0) {
        contents = handle_account_create(params, client_proxy);
    } else if cmd.find("account_list")>=Some(0) {
        contents = handle_account_list(params, client_proxy);
    } else if cmd.find("account_mint")>=Some(0) {
        contents = handle_account_mint(params, client_proxy);
    } else if cmd.find("query_balance")>=Some(0) {
        contents = handle_query_balance(params, client_proxy);
    } else if cmd.find("query_sequence")>=Some(0) {
        contents = handle_query_sequence(params, client_proxy);
    } else if cmd.find("transfer")>=Some(0) {
        contents = handle_transfer(params, client_proxy);
    } else if cmd.find("query_txn_acc_seq")>=Some(0) {
        contents = handle_query_txn_acc_seq(params, client_proxy);
    }
    let response = format!("HTTP/1.1 200 OK\r\n\r\n{}", contents);
    stream.write(response.as_bytes()).unwrap();
    stream.flush().unwrap();
}

/**
* 获取请求中的Query String,规定参数以?cmd=开头
* @version v0.0.1 闫涛 2019.06.23
*/
fn get_query_string(request: &str) -> String {
    let pos = request.find("?cmd=");
    if pos <= Some(0) {
        return "Has no parameters in request".to_string();
    }
    let end_pos = request.find(" HTTP/1.1");
    return (&request[(pos.unwrap()+1)..end_pos.unwrap()]).to_string();
}

/**
* 获取请求cmd参数值
* @version v0.0.1 闫涛 2019.06.23
*/
fn get_cmd_param(query_string: String) -> String {
    let params: Vec<_> = query_string.split("&").collect();
    for param in params.iter() {
        println!("item: {}!", param);
        if param.find("cmd=") >= Some(0) {
            let cmd = &param[4..];
            return cmd.to_string();
        }
    }
    return "".to_string();
}

与前一篇文章中的程序不同点在于,调用每个命令处理函数时,需将client_proxy作为参数传入。有了这些公共方法之后,我们就可以开始实现各个命令处理函数了。

账户生成

在命令行模式下,我们通过account create来创建账户,在我们的web服务器中我们通过http://127.0.0.1:7878/ls?cmd=account_create来创建账户,命令处理代码如下所示:

/**
* 生成账户命令处理函数
* @version v0.0.1 闫涛 2019.06.23
*/
fn handle_account_create(_params: Vec<&str>, client_proxy: &mut ClientProxy) -> String {
    println!("生成新账户!");
    let mut rst: String;
    match client_proxy.create_next_account() {
        Ok(account_data) => {
            rst = format!("{{\"account_id\": \"{}\", \"wallet_address\": \"{}\"}}", 
                    account_data.index, hex::encode(account_data.address));
        },
        Err(e) => rst = format!("Error creating account:{}", e),
    }
    return rst;
}

在这个例子中使用了Rust的一种比较特别的语法,我们用match修饰调用client_proxy.create_next_account方法,该方法会返回Ok或Err两种情况,我们需要分别进行处理,大家可以将match 理解为其他语言中的switch语句。

查询所有账户列表

我们通过http://127.0.0.1:7878/ls?cmd=account_list请求来获取所有账户列表,命令处理函数如下所示:

/**
* 列出当前系统的所有账户
* @version v0.0.1 闫涛 2019.06.23
*/
fn handle_account_list(params: Vec<&str>, client_proxy: &ClientProxy) -> String {
    return client_proxy.get_all_accounts();
}

我们在client/src/client_proxy.rs中添加一个新的函数,如下所示:

    /// Print index and address of all accounts.
    pub fn get_all_accounts(&self) -> String {
        let mut rst: String = String::new();
        rst.push_str("[");

        if !self.accounts.is_empty() {
            for (ref index, ref account) in self.accounts.iter().enumerate() {
                let mut item: String = String::new();
                item.push_str(&format!("{{\"account_id\":{}, \"wallet_address\":\"{}\", \"account_seq\":{}, \"account_status\":\"{:?}\"}},", 
                        index, hex::encode(&account.address), 
                        account.sequence_number, account.status));
                rst.push_str(&item);
            }
        }

        if let Some(faucet_account) = &self.faucet_account {
            println!(
                "Faucet account address: {}, sequence_number: {}, status: {:?}",
                hex::encode(&faucet_account.address),
                faucet_account.sequence_number,
                faucet_account.status,
            );
        }
        rst.push_str("]");
        return rst;
    }

本来在官方程序中,有一个print_all_accounts方法,但是该方法只是将信息打印到屏幕上,这显然不是我们所需要的,所以我们重写了这个函数,将其返回值定义为Json字符串。

挖矿和发币

我们通过http://127.0.0.1:7878/ls?cmd=account_mint&account_id=1&num_coins=100来进行挖矿和发币,表明向编号为1的钱包发送100个Libra币,命令处理函数如下所示:

/**
* 挖指定数量的币发送给指定的账户
* @version v0.0.1 闫涛 2019.06.23
*/
fn handle_account_mint(params: Vec<&str>, client_proxy: &mut ClientProxy) -> String {
    let mut account_id: String = String::new();
    let mut num_coins: String = String::new();
    for param in params.iter() {
        if param.find("account_id=") >= Some(0) {
            account_id.push_str(&param[11..]);
        } else if param.find("num_coins=") >= Some(0) {
            num_coins.push_str(&param[10..]);
        }
    }
    println!("挖矿发币:account_id={}; num_coins={}!", account_id, num_coins);
    let cmd = format!("mint {} {}", account_id, num_coins);
    let params = ["mint", account_id.as_str(), num_coins.as_str()];
    match client_proxy.account_mint_coins(&params, false) {
        Ok(msg) => return msg,
        Err(_) => "{{\"status\": \"Error\"}}".to_string()
    }
}

查询账户余额

我们通过http://127.0.0.1:7878/ls?cmd=query_balance&account_id=1来查询账户编号为1的账户的余额,命令处理函数如下所示:

/**
* 查询账户余额
* @version v0.0.1 闫涛 2019.06.23
*/
fn handle_query_balance(params: Vec<&str>, client_proxy: &mut ClientProxy) -> String {
    let mut account_id: String = String::new();
    for param in params.iter() {
        if param.find("account_id=") >= Some(0) {
            account_id.push_str(&param[11..]);
        } 
    }
    println!("查询余额:account_id={};!", account_id);
    let params = ["balance", account_id.as_str()];
    match client_proxy.get_balance(&params) {
        Ok(num) => {
            let resp = format!("{{\"status\": \"Ok\", \"balance\": {} }}", num);
            return resp;
        },
        Err(_) => "{{\"status\": \"Error\"}}".to_string()
    }
}

查询账户最新交易编号

我们通过http://127.0.0.1:7878/ls?cmd=query_sequence&account_id=1查询账户编号为1的账户的交易编号,注意这里只有资金转出时才产生交易,命令处理方法如下所示:

/**
* 查询指定账户的交易编号,即已经发生的交易数(指转出的笔数)
* @version v0.0.1 闫涛 2019.06.23
*/
fn handle_query_sequence(params: Vec<&str>, client_proxy: &mut ClientProxy) -> String {
    let mut account_id: String = String::new();
    for param in params.iter() {
        if param.find("account_id=") >= Some(0) {
            account_id.push_str(&param[11..]);
        }
    }
    println!("查询交易编号:account_id={};!", account_id);
    let params = ["sequence", account_id.as_str()];
    match client_proxy.get_sequence_number(&params) {
        Ok(seq) => {
            let resp = format!("{{\"status\": \"Ok\", \"sequence\": {} }}", seq);
            return resp;
        },
        Err(_) => "{{\"status\": \"Error\"}}".to_string()
    }
}

转账

我们通过http://127.0.0.1:7878/ls?cmd=transfer&src_account_id=1&dest_account_id=2&amount=20来实现从账户1转20个Libra币给账户2,命令处理函数如下所示:

/**
* 账户之间转账
* @version v0.0.1 闫涛 2019.06.23
*/
fn handle_transfer(params: Vec<&str>, client_proxy: &mut ClientProxy) -> String {
    let mut src_account_id: String = String::new();
    let mut dest_account_id: String = String::new();
    let mut amount: String = String::new();
    for param in params.iter() {
        if param.find("src_account_id=") >= Some(0) {
            src_account_id.push_str(&param[15..]);
        } else if param.find("dest_account_id=") >= Some(0) {
            dest_account_id.push_str(&param[16..]);
        } else if param.find("amount=") >= Some(0) {
            amount.push_str(&param[7..]);
        }
    }
    println!("账户间转账交易:src_account_id={}; dest_account_id={}; amount={}!", src_account_id, dest_account_id, amount);
    let params = ["transfer", src_account_id.as_str(),
        dest_account_id.as_str(),
        amount.as_str()
    ];
    match client_proxy.transfer_coins(&params, false) {
        Ok(ias) => {
            let resp = format!("{{\"status\": \"Ok\", \"account_index\": {}, \"account_number\": {} }}", ias.account_index, ias.sequence_number);
            return resp;
        },
        Err(_) => "{{\"status\": \"Error\"}}".to_string()
    }
}

查询交易详细信息

我们通过http://127.0.0.1:7878/ls?cmd=query_txn_acc_seq&account_id=1&seq=1用来查询账户1的编号为1的交易的详情,命令处理函数如下所示:

/**
* 查询交易详情
* @version v0.0.1 闫涛 2019.06.23
*/
fn handle_query_txn_acc_seq(params: Vec<&str>, client_proxy: &mut ClientProxy) -> String {
    let mut account_id: String = String::new();
    let mut seq: String = String::new();
    for param in params.iter() {
        if param.find("account_id=") >= Some(0) {
            account_id.push_str(&param[11..]);
        } else if param.find("seq=") >= Some(0) {
            seq.push_str(&param[4..]);
        }
    }
    println!("查询交易详情:account_id={}; seq={}!", account_id, seq);
    let params = ["txn_acc_seq", account_id.as_str(), seq.as_str(), "false"];
    match client_proxy.get_committed_txn_by_acc_seq(&params) {
        Ok(rst) => {
            let mut resp = String::new(); //format!("{{\"status\": \"Ok\" }}");
            if let Some(obj) = rst {
                let trans = obj.0;
                resp.push_str(&trans.format_for_client(name_cb));
            }
            return resp;
        },
        Err(_) => "{{\"status\": \"Error\"}}".to_string()
    }
}

这样,我们就拥有了一个与官方命令功能相等的Web API,虽然十分简陋,但是作为应用系统测试后台已经足够了,我们可以现在就开始构建基于Libra的应用系统,祝大家能抓住Libra的商机。

### 回答1: Libra R-CNN是Facebook AI Research团队于2019年提出的一种目标检测算法,它采用了一种新的思路——将目标检测过程分为两个阶段:先使用高效的RPN网络生成候选框,再使用分类器对候选框进行分类和回归。这种方法可以大大提高检测效率,同时保持较高的精度。与传统的Faster R-CNN相比,Libra R-CNN的检测速度快了近3倍,同时保持了相当的精度。 ### 回答2: Libra R-CNN是由Facebook AI Research团队在2019年提出的一种目标检测算法。目标检测是计算机视觉中的一个重要任务,旨在识别和定位图像中的不同目标。 Libra R-CNN的设计目标是解决目标检测中存在的两个挑战:不平衡的目标类别分布和多尺度目标的检测问题。在目标类别分布上,一些类别的目标在数据集中往往是非常罕见的,这导致传统的目标检测算法在罕见类别上的性能较差。而在多尺度目标的检测上,由于图像中的目标可能具有不同的尺度,传统的目标检测算法在检测小目标和大目标时往往表现不佳。 Libra R-CNN通过引入新的损失函数和网络结构来解决这两个问题。其中,采用了一种新的正负样本选择策略来平衡不同目标类别的分布,使得模型能够更好地处理罕见类别的目标。此外,通过引入一个多尺度特征融合模块,Libra R-CNN能够有效地处理不同尺度的目标,提高目标检测的性能。 实验证明,Libra R-CNN在多个目标检测数据集上取得了很好的性能表现,相较于其他目标检测算法具有更好的检测准确性和鲁棒性。该算法在推动目标检测技术的发展上具有重要的意义,为解决目标检测中的挑战提供了新的思路和方法。 总之,Libra R-CNN是一种解决目标检测中不平衡目标类别分布和多尺度目标检测问题的算法,通过引入新的损失函数和网络结构,取得了很好的性能表现。它对于推动目标检测技术的发展具有重要的意义。 ### 回答3: Libra R-CNN是一种基于目标检测和实例分割的深度学习模型算法。它被设计用于解决目标检测中存在的物体不平衡问题,即训练样本中不同类别的物体数量差异过大。通常,在目标检测任务中,一些常见的物体类别(如人、车)的样本数量会远远超过其他类别,这会导致模型过度关注于此类常见类别而忽略其他类别。而Libra R-CNN就是为了解决这个问题而提出的。 Libra R-CNN的核心思想是通过引入一种新的调节因子,对损失函数进行重新定义,从而有效平衡不同类别的物体样本的权重。这个调节因子综合考虑了每个类别的物体例子在整个训练集中出现的频率,以及目标检测任务中不同类别之间的难易程度。通过对不同类别样本的权重进行调整,Libra R-CNN能够更加均衡地对待训练集中的不同类别物体,提高模型对少样本类别的检测能力。 此外,Libra R-CNN还引入了一种新的网络结构,叫做Libra引导采样(Libra RoI Sampling)。这个结构能够根据样本的难易程度,自适应地调整样本的采样比例,让模型更加关注难以检测的类别。通过这种方式,Libra R-CNN在目标检测中取得了较好的性能。 综上所述,Libra R-CNN是一种通过引入调节因子和采样策略来解决目标检测中物体不平衡问题的算法模型。它的提出有效地解决了训练样本中不同类别物体数量差异过大的问题,提高了对少样本类别的检测能力,并在目标检测任务中取得了较好的性能。
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值