主页 > imtoken官方版下载安卓最新版 > 转账:比特币(BTC)连接到数字货币兑换钱包

转账:比特币(BTC)连接到数字货币兑换钱包

本文转自【Biyan BIZZAN Blockchain】比特币(BTC)连接数字货币交易所的钱包

BIZZAN在开发数字货币交易所的过程中,有两大难点,一是高速撮合交易引擎,二是钱包对接,这两个是我们团队之前没有接触过的。本系列文章主要介绍数字货币交易所钱包对接的实现技术。首先要连接的是比特币BTC,因为BTC的提现过程比较简单。用户在交易所提交提现请求,然后由交易所审核,然后系统或人工发币。

交易所如何连接到比特币钱包

交易所连接比特币钱包有两种方式:

1. 自建BTC节点

BTC节点支持通过RPC访问,所以自建节点可以快速查询区块和交易。但是由于BTC节点会同步所有区块,截至目前(2018/09/10)区块高度已经达到50万比特币链接充值,所以这需要大量的硬盘存储空间,16年至少160G。如果各币种自建节点,占用大量硬盘存储空间,还要承担维护节点的工作,创业团队对资金的合理使用要求比较高,所以我们放弃了这种方式,采用了第二种方式。

比特币链接充值_比特币钱包 充值_比特币交易网怎么充值

2. 第三方API+区块链浏览器

通过第三方API可以对Bit钱包进行操作,如生成密钥对、玩币等。同时也可以通过区块链浏览器提供的开放接口进行交易查询。因此,我们团队采用这种方式连接比特币钱包。

比特币钱包知识点

1.比特币钱包密钥生成

这里需要了解的知识点是比特币钱包:首先,比特币钱包可以离线生成,并且通过一系列的加密操作比特币链接充值,如果不介意的话,也可以自己实现,但是有无需重复轮子;其次,比特币钱包的离线生成也可以保证不重复。你不必担心比特币地址会用完,因为理论上,比特币钱包可以生成的地址比宇宙中所有原子的总和还要多,所以完全不必担心与其他人离线生成重复的钱包地址。.

比特币交易网怎么充值_比特币钱包 充值_比特币链接充值

2.比特币网络中没有账户的概念

如果你接触过以太坊,你就会知道以太坊有账户的概念。账户的概念更接近我们的现实生活。使用帐户,您可以查看余额。但比特币没有这样的概念。如果你有兴趣,可以研究一下比特币中的 UTXO。比特币只有交易,所以你很好奇,如果你只有一个流水账,你怎么知道一个地址的余额?比特币是通过计算您钱包地址的所有转入和所有转出之间的差异来计算的。您可能还想问,将 10 个比特币转移到一个地址的过程是什么?首先,比特币网络会计算你当前的余额,看它是否足够转账。如果足够了,那么比特币网络会将之前转移的交易转移到你的地址,称为输入,即输入,然后将你转移到其他地址。交易作为输出,即输出。我们可以通过区块链浏览器blockchain.info提供的API查询如下结果:

 {
    "hash":"b6f6991d03df0e2e04dafffcd6bc418aac66049e2cd74b80f14ac86db1e3f0da",
    "ver":1,
    "vin_sz":1,
    "vout_sz":2,
    "lock_time":"Unavailable",
    "size":258,
    "relayed_by":"64.179.201.80",
    "block_height, 12200,
    "tx_index":"12563028",
    "inputs":[
            {
                "prev_out":{
                    "hash":"a3e2bcc9a5f776112497a32b05f4b9e5b2405ed9",
                    "value":"100000000",
                    "tx_index":"12554260",
                    "n":"2"
                },
                "script":"76a914641ad5051edd97029a003fe9efb29359fcee409d88ac"
            }
        ],
    "out":[
                {
                    "value":"98000000",
                    "hash":"29d6a3540acfa0a950bef2bfdc75cd51c24390fd",
                    "script":"76a914641ad5051edd97029a003fe9efb29359fcee409d88ac"
                },
                {
                    "value":"2000000",
                    "hash":"17b5038a413f5c5ee288caa64cfab35a0c01914e",
                    "script":"76a914641ad5051edd97029a003fe9efb29359fcee409d88ac"
                }
        ]
 }

查询链接:

以上是查询一个事务(Transaction)的结果。您可以看到 int 和 out 包含多个结果。

比特币链接充值_比特币钱包 充值_比特币交易网怎么充值

交易所充值逻辑设计

上图是交易所充值逻辑的设计图。以下是该过程的简要说明:

在这里插入图片描述

首先,我们通过比特币钱包API批量生成10000个比特币钱包地址。当用户在交易所获得自己的BTC充值地址时,我们从这10000个地址中给他分配一个(地址绑定UID)

然后,我们会提前运行一个监控任务程序。该程序负责从比特币区块链浏览器获取最新的区块信息,然后解析交易中涉及的地址。当我们发现我们分配的地址出现在输出中时,我们会将 BTC 余额添加到与该地址绑定的用户资产表中。

比特币钱包 充值_比特币交易网怎么充值_比特币链接充值

最后提醒用户充值。

实现方法代码演示(Java)

1. 通过第三方钱包 API 生成比特币地址:

这里我们使用开源代码bitcoinj,通过maven导入这个库,示例代码如下

import java.io.File;
import java.io.IOException;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.wallet.Wallet;
/**
 * 演示生成/保存密钥对和钱包地址到文件
 * @author bizzan.com
 *
 */
public class GenerateKeyPair {
    
    public static void main(String[] args) {
        
        // 如在主网中生成钱包密钥对,需要改为MainNetParms
        NetworkParameters params  = TestNet3Params.get(); 
        
        Wallet wallet = null;
        // 创建一个文件用于保存钱包文件
        final File walletFile = new File("test.wallet");
        
        wallet = new Wallet(params);
        
        // 循环生成10个密钥对并添加到钱包
        for(int i = 0; i < 10000; i++) {
            ECKey key = new ECKey();
            wallet.importKey(key);
        }
        try {
            // 保存钱包文件
            wallet.saveToFile(walletFile);
            // 打印钱包内容信息
            System.out.println(wallet.toString(true, true, true, null));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

比特币钱包 充值_比特币链接充值_比特币交易网怎么充值

使用此代码,您可以生成 10,000 个钱包地址,您可以将其保存在文件中。在实际应用中,您还需要在数据库中保存 10,000 个钱包地址,但不能将私钥保存在联网数据库中。

您保存到文件中的钱包内容可以通过以下代码读取:

import java.io.File;
import org.bitcoinj.core.ECKey;
import org.bitcoinj.core.NetworkParameters;
import org.bitcoinj.params.TestNet3Params;
import org.bitcoinj.wallet.UnreadableWalletException;
import org.bitcoinj.wallet.Wallet;
/**
 * 从文件导入钱包,注意需要先运行GenerateKeyPair生成钱包文件
 * @author bizzan.com
 *
 */
public class ImportWalletFromFile {
    public static void main(String[] args) {
        // 如在主网中生成钱包密钥对,需要改为MainNetParms
        NetworkParameters params  = TestNet3Params.get(); 
        
        Wallet wallet = null;
        // 创建一个文件用于保存钱包文件
        final File walletFile = new File("test.wallet");
        
        try {
            // 从文件加载钱包
            wallet = Wallet.loadFromFile(walletFile);
            // 打印钱包信息
            System.out.println(wallet.toString(true, true, true, null));
            
            // 获取钱包第一个密钥对
            ECKey firstKey = wallet.getImportedKeys().get(0);
            // 打印密钥对信息
            System.out.println("The first key is: \n" + firstKey.toString());
            // 打印密钥对中的私钥(HEX)
            System.out.println("The first key Private Key(HEX) is: " + firstKey.getPrivateKeyAsHex());
            // 打印密钥对中的私钥(WIF=Wallet Import Format)
            System.out.println("The first key Private Key(WIF) is: " + firstKey.getPrivateKeyAsWiF(params));
            // 打印密钥对中的公钥:
            System.out.println("The first key Public Key is: " +firstKey.getPubKey());
            // 打印密钥对中的公钥(HEX)
            System.out.println("The first key Public Key(HEX) is: " + firstKey.getPublicKeyAsHex());
            // 打印密钥对中的公钥(Hash)
            System.out.println("The first key Public Key(Hash) is: " + firstKey.getPubKeyHash());
            // 打印密钥对钱包地址
            System.out.println("The first key Wallet Address is: " + firstKey.toAddress(params));
            
            
            // 通过公钥查找密钥对:findKeyFromPubHash(byte[] pubkeyHash)
            ECKey resultKey1 = wallet.findKeyFromPubHash(firstKey.getPubKeyHash());
            System.out.println("Find Key From PubHash: " + resultKey1.toString());
            // 判断公钥是否在钱包:isPubKeyHashMine(byte[] pubkeyHash)
            System.out.println("Is PubKeyHash Mine: " + wallet.isPubKeyHashMine(firstKey.getPubKeyHash()));
            // 通过公钥查找密钥对:findKeyFromPubKey(byte[] pubkey)
            ECKey resultKey2 = wallet.findKeyFromPubKey(firstKey.getPubKey());
            System.out.println("Find Key From PubKey: " + resultKey2.toString());
            // 判断公钥是否在钱包:isPubKeyMine(byte[] pubkey)
            System.out.println("Is PubKey Mine: " + wallet.isPubKeyMine(firstKey.getPubKey()));
        } catch (UnreadableWalletException e) {
            e.printStackTrace();
        }
    }
}

2. 通过区块链浏览器监控钱包地址:

通过轮询,比如每隔1小时查询区块链浏览器获取最新的区块信息,然后解析其中的地址信息,示例代码如下:

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import org.apache.http.HttpEntity;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
/**
 * 演示通过Blockchain.info提供的JSON Http获取比特币区块信息
 * @author bizzan.com
 *
 */
public class ApiApp {
    private final static String requstHttpUrl = "https://blockchain.info/";
    
    public static void main(String[] args) {
        String param = "block-height/500000?format=json";
        String result = sendGet(requstHttpUrl, param);
        
        JSONObject jsonObject = JSONObject.parseObject(result);
        JSONArray arr = jsonObject.getJSONArray("blocks");
        
        JSONArray txList = arr.getJSONObject(0).getJSONArray("tx");
        for(int i = 0; i < txList.size(); i++) {
            JSONObject txObj = txList.getJSONObject(i);
            JSONArray outs = txObj.getJSONArray("out");
            for(int j = 0; j < outs.size(); j++) {
                JSONObject outObj = outs.getJSONObject(j);
                // 这里已获取交易信息(包含收币地址信息),由此可将此地址与充值地址库对比,进行充值后续工作
                System.out.println("tx_index:" + outObj.getIntValue("tx_index") + " - addr:" + outObj.getString("addr"));
            }
        }
    }
    
    public static String sendGet(String url, String param) {
        CloseableHttpClient httpClient = null;
        CloseableHttpResponse response = null;
        String result = "";
        try {
            httpClient = HttpClients.createDefault();
            HttpGet httpGet = new HttpGet(url + param);
            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(35000)
                    .setConnectionRequestTimeout(35000)
                    .setSocketTimeout(60000)
                    .build();
            httpGet.setConfig(requestConfig);
            response = httpClient.execute(httpGet);
            HttpEntity entity = response.getEntity();
            result = EntityUtils.toString(entity);
        } catch (ClientProtocolException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != response) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != httpClient) {
                try {
                    httpClient.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return result;
    }
}

上面的代码是获取高度为500000的区块信息并解析其中的地址。