java编写比特币挖矿程序,stratum协议通信

内容仅供研究学习与娱乐,虚拟货币挖矿属于国家淘汰类落后生产工艺装备。

源码地址:https://gitee.com/changjiuxiong/btcMinerJava

https://github.com/changjiuxiong/btcMinerJava

本文将实现使用java语言编写完整可用的比特币挖矿程序(有时可简称矿机)。通过Stratum协议与矿池通信。Stratum协议是一种用于挖矿客户端和矿池之间的通信协议,旨在通过TCP连接进行通信,允许矿池动态地给矿工分配挖矿任务,并且能够实时更新挖矿难度。目前主流的矿池与矿机都是采用stratum协议。这里默认指stratum的v1版,v2版还未完全制定好,也未被大多数矿池采用。

若想详细了解stratum协议,这是中文文档:

slush_stratum_protocol_zhCN/main.md at master · pangsitao/slush_stratum_protocol_zhCN · GitHub

源码解析:

第一步,连接矿池

private static final String POOL_HOST = "public-pool.io";
private static final int POOL_PORT = 21496;
......
socket = new Socket(POOL_HOST, POOL_PORT);
in = new BufferedReader(new InputStreamReader(socket.getInputStream(),StandardCharsets.UTF_8));
out = new OutputStreamWriter(socket.getOutputStream(), StandardCharsets.UTF_8);

这里使用Socket连接一个比特币solo矿池,然后保存输入输出流后面与矿池通信。这里你可以修改矿池地址和端口,选择你喜欢的矿池进行挖矿。我选public-pool是因为它允许矿机自定义挖矿难度。查找矿池地址可以参考这个网站:Bitcoin (BTC) SHA-256 | Mining Pools

第二步,订阅矿池

JSONObject subscribeRequest = new JSONObject();
subscribeRequest.put("id", 1);
subscribeRequest.put("method", "mining.subscribe");
subscribeRequest.put("params", new Object[]{"javaminer"});
out.write(subscribeRequest.toString() + "\n");
out.flush();
String subscribeResponse = in.readLine();

if (subscribeResponse != null) {
            JSONObject message = new JSONObject(subscribeResponse);
            if (message.has("result")) {
                Extranonce1 = message.getJSONArray("result").getString(1);
                Extranonce2_size = message.getJSONArray("result").getInt(2);
            }
}

发送一条订阅消息给矿池,像这样:

{"id": 1, "method": "mining.subscribe", "params": []}

给我们的矿机起了个个性化型号javaminer,有些矿池可能会统计型号。然后收到矿池的回复消息,像这样:

{"id": 1, "result": [ [ ["mining.set_difficulty", "b4b6693b72a50c7116db18d6497cac52"], ["mining.notify", "ae6812eb4cd7735a302a8a9dd95cf71f"]], "08000002", 4], "error": null}

保存"08000002"、4,作为有用的变量,后面挖矿时会用到。

第三步,获取授权

private static final String USERNAME = "bc1qst559j7hf7xfasnlvs8sdjuvltss0at0h0h7pk.javaminerhomeMult";
private static final String PASSWORD = "123456";
......
JSONObject authorizeRequest = new JSONObject();
authorizeRequest.put("id", 2);
authorizeRequest.put("method", "mining.authorize");
authorizeRequest.put("params", new Object[]{USERNAME, PASSWORD});
out.write(authorizeRequest.toString() + "\n");
out.flush();
String authorizeResponse = in.readLine();
if (authorizeResponse != null) {
            JSONObject message = new JSONObject(authorizeResponse);
            if (message.has("result")) {
            }else if (message.has("method")) {
                String method = message.getString("method");
                if(method.equals("mining.set_difficulty")){
                    if (message.has("params")){
                        double diff = message.getJSONArray("params").getDouble(0);
                        setDifficulty(diff);
                    }
                }

            }
}

告诉矿池,自己的比特币钱包地址,矿工名字(可不填),矿池密码(可乱填),像这样: 

{"params": ["bc1qst559j7hf7xfasnlvs8sdjuvltss0at0h0h7pk.javaminerhomeMult", "123456"], "id": 2, "method": "mining.authorize"}

这里你需要把bc1qst559j7hf7xfasnlvs8sdjuvltss0at0h0h7pk改成你的比特币钱包地址,不然挖到比特币就进我的钱包了。钱包地址.矿工名字,密码任意填。然后收到矿池的回复,又两种可能,一种是“好的收到”,这种不用管,第二种是矿池回复了你当前矿池的工作难度值difficulty,像这样:

{ "id": null, "method": "mining.set_difficulty", "params": [2]}

表示难度值是2,我们记录下来difficulty,后面挖矿要用到。 

第三步,开始挖矿

完成前面的认证后,我们就一直监听输出流out有没有消息,当收到类型这样的消息:

{"params": ["bf", "4d16b6f85af6e2198f44ae2a6de67f78487ae5611b77c6c0440b921e00000000",
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff20020862062f503253482f04b8864e5008",
"072f736c7573682f000000000100f2052a010000001976a914d23fcdf86f7e756a64a7a9688ef9903327048ed988ac00000000", [],
"00000002", "1c2ac4af", "504e86b9", false], "id": null, "method": "mining.notify"}

 表示矿池下发了新的工作任务,我们可以开始挖矿了。

params里面的值,按顺序是以下内容:

  • job_id - 任务的ID。在上传生成的share时需要使用。
  • prevhash - 前一个区块的哈希。
  • coinb1 - coinbase币基交易的最初部分
  • coinb2 - coinbase币基交易的最后部分
  • merkle_branch - merkle树的列表,用于计算merkle根。不是所有交易数据的列表,只包含merkle树算法的各步的准备好的哈希值。merkle树如何计算的?参考资料:Merkle tree及其在区块链等领域的应用_merkel tree的构建,有什么实际应用价值-优快云博客
  • version - 比特币区块版本。
  • nbits - 编码了当前网络的难度
  • ntime - 时间
  • clean_jobs - 如果此字段为true,服务器意思是说,这个任务之前的任务过期了,提交旧工作会被拒绝。如果这个标记是true,矿机应该丢掉之前所有的工作任务,开始这个新的工作。如果此字段为false,矿机可以继续之前的工作,不用立即切换到这个新工作。

有了这些值,我们开始挖矿:

首先根据difficulty计算挖矿目标值target

挖矿就是计算区块头的哈希值,使得哈希值<target

区块头=version + prevHashl + merkleRoot + ntime + nbits + nonce。其中nonce是可变的,我们不断改变nonce,然后计算hash=sha256D(区块头),直到hash<target,就可以提交工作了。如果恰好你的hash<比特币主网target,恭喜你挖到一个新的区块,获得3.125比特币(大约人民币220万元)。

其中,merkleRoot是从merkle_branch和coinbase得到的,代码如下:

public static String getMerkle_root00(String coinbase, String[] merkle_branch){
        byte [] res = sha256D(hexStringToByteArray(coinbase));
        for(String s : merkle_branch){
            byte[] sByte = hexStringToByteArray(s);
            byte [] temp = new byte[64];
            System.arraycopy(res,0,temp,0,32);
            System.arraycopy(sByte,0,temp,32,32);
            res = sha256D(temp);
        }
        String bigEnd = bytesToHex(res);
        return bigEnd;
    }
String coinbase = coinb1 + Extranonce1 + extranonce2 + coinb2;

extranonce2也是一个随机数,字节长度是Extranonce2_size(前面提到的已知的变量)

所以总的来说我们先根据Extranonce2_size生成一个随机数extranonce2,然后算出coinbase,然后算出merkleRoot,然后随机生成一个nonce,然后得到区块头head,然后计算sha256D(区块头),重复执行直到sha256D(区块头)<target

好了,你已经掌握了比特币挖矿的全部知识,快去挖光比特币吧!

源码中还有很多细节,比如多线程、大小端的处理、奇怪的各种字节串反转,都是使用java语言适配挖矿协议的天坑,里面就有很多比特币源码设计出的屎山。我敢说这是世界上第一个能用的java挖矿程序。

CPU哈希率约为10MH/s,因此不难计算出你有1/700000000000000的机会挖掘下一个区块,区块奖励目前为3.125比特币。忘记概率和奖励,玩得开心,祝你好运!

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值