比特幣地址是怎樣生成的

首先獲得 ECDSA private key, 再獲得 ECDSA public key,,而後再計算出錢包地址。ECDSA是Elliptic Curve Digital Signature Algorithm的縮寫, 即橢圓曲線數字簽名算法。java

一、ECDSA private key:執行openssl命令, bitcoin要用到secp256k1git

openssl ecparam -name secp256k1 -genkey > priv.pem

# DER格式
openssl ec -in priv.pem -outform DER | tail -c +8 | head -c 32 | xxd -p -c 32

# 輸出
read EC key
writing EC key
ccea9c5a20e2b78c2e0fbdd8ae2d2b67e6b1894ccb7a55fc1de08bd53994ea64

獲得祕鑰文件priv.pem, 輸出DER格式, 長度是 64
ccea9c5a20e2b78c2e0fbdd8ae2d2b67e6b1894ccb7a55fc1de08bd53994ea64算法

 

二、ECDSA public key: priv.pem 生成 pub_keybash

openssl ec -in priv.pem -pubout -outform DER | tail -c 65 | xxd -p -c 65

# 輸出
read EC key
writing EC key
04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d

一樣輸出DER格式, 長度是130
pub_key = 04d061e9c5891f579fd548cfd22ff29f5c642714cc7e7a9215f0071ef5a5723f691757b28e31be71f09f24673eed52348e58d53bcfd26f4d96ec6bf1489eab429d編碼

三、第2步結果進行hash160運算:hash160運算就是先進行SHA256, 再進行RMD160code

bytes = [pub_key].pack("H*") # 轉爲16進制
hash160_val = Digest::RMD160.hexdigest(Digest::SHA256.digest(bytes) )

hash160_val = 2b6f3b9e337cedbb7c40839523fb1100709c12f7orm

四、第3步結果加上前綴符blog

前綴符通常是00, 會生成普通的主網地址
bitcoin address 前綴符有好幾種, 具體看https://en.bitcoin.it/wiki/List_of_address_prefixesip

'00'+ '2b6f3b9e337cedbb7c40839523fb1100709c12f7'

result_04 = 002b6f3b9e337cedbb7c40839523fb1100709c12f7ssl

bitcoin地址的前綴列表列出了幾種錢包地址的類型

十進制 16進制 做用 首字母 例子
0 00 P2PKH address 1 17VZNX1SN5NtKa8UQFxwQbFeFc3iqRYhem
5 05 P2SH address 3 3EktnHQD7RiAE6uzMj2ZifT9YgRrkSgzQX
111 6F Testnet pub key m or n mipcBbFg9gMiCh81Kj8tqqdgoZub1ZJRfn

五、第4步結果, 執行2次SHA256, 取前8位做爲校驗和

hex_str = [result_04].pack("H*")
checksum = Digest::SHA256.hexdigest(Digest::SHA256.digest(hex_str) )[0...8]

checksum = 86b2e90c

六、第4步結果 跟 第5步結果合併

'002b6f3b9e337cedbb7c40839523fb1100709c12f7' + '86b2e90c'
# result_04 + checksum

result_06 = 002b6f3b9e337cedbb7c40839523fb1100709c12f786b2e90c

七、第6步結果進行base58編碼

Base58是一種獨特的編碼方式, 是Base64的變形, 主要用於Bitcoin的錢包地址.
相比Base64, Base58去掉了數字0, 大寫字母O, 大寫字母I, 小寫字母l+/, 避免引發視覺混淆。

result_06 = "002b6f3b9e337cedbb7c40839523fb1100709c12f786b2e90c"
leading_zero_bytes = (step_06.match(/^([0]+)/) ? $1 : '').size / 2
# leading_zero_bytes的做用是字母填充, 待研究下

address = ("1" * leading_zero_bytes) + encode_base58(step_06.to_i(16) )

獲得 14xfJr1DArtYR156XBs28FoYk6sQqirT2s, 這就是了一個標準的bitcoin地址。

在bitcoin系統中,私鑰能得公鑰, 公鑰能獲得錢包地址,
私鑰=>公鑰=>錢包地址, 而反向是不可能的。

 

How to create Bitcoin Address

 

Java Base58算法

public class Base58 {
    public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
    private static final char ENCODED_ZERO;
    private static final int[] INDEXES;

    public Base58() {
    }

    public static String encode(byte[] input) {
        if (input.length == 0) {
            return "";
        } else {
            int zeros;
            for(zeros = 0; zeros < input.length && input[zeros] == 0; ++zeros) {
                ;
            }

            input = Arrays.copyOf(input, input.length);
            char[] encoded = new char[input.length * 2];
            int outputStart = encoded.length;
            int inputStart = zeros;

            while(inputStart < input.length) {
                --outputStart;
                encoded[outputStart] = ALPHABET[divmod(input, inputStart, 256, 58)];
                if (input[inputStart] == 0) {
                    ++inputStart;
                }
            }

            while(outputStart < encoded.length && encoded[outputStart] == ENCODED_ZERO) {
                ++outputStart;
            }

            while(true) {
                --zeros;
                if (zeros < 0) {
                    return new String(encoded, outputStart, encoded.length - outputStart);
                }

                --outputStart;
                encoded[outputStart] = ENCODED_ZERO;
            }
        }
    }

    public static byte[] decode(String input) throws AddressFormatException {
        if (input.length() == 0) {
            return new byte[0];
        } else {
            byte[] input58 = new byte[input.length()];

            int zeros;
            int outputStart;
            for(zeros = 0; zeros < input.length(); ++zeros) {
                char c = input.charAt(zeros);
                outputStart = c < 128 ? INDEXES[c] : -1;
                if (outputStart < 0) {
                    throw new AddressFormatException("Illegal character " + c + " at position " + zeros);
                }

                input58[zeros] = (byte)outputStart;
            }

            for(zeros = 0; zeros < input58.length && input58[zeros] == 0; ++zeros) {
                ;
            }

            byte[] decoded = new byte[input.length()];
            outputStart = decoded.length;
            int inputStart = zeros;

            while(inputStart < input58.length) {
                --outputStart;
                decoded[outputStart] = divmod(input58, inputStart, 58, 256);
                if (input58[inputStart] == 0) {
                    ++inputStart;
                }
            }

            while(outputStart < decoded.length && decoded[outputStart] == 0) {
                ++outputStart;
            }

            return Arrays.copyOfRange(decoded, outputStart - zeros, decoded.length);
        }
    }

    public static BigInteger decodeToBigInteger(String input) throws AddressFormatException {
        return new BigInteger(1, decode(input));
    }

    public static byte[] decodeChecked(String input) throws AddressFormatException {
        byte[] decoded = decode(input);
        if (decoded.length < 4) {
            throw new AddressFormatException("Input too short");
        } else {
            byte[] data = Arrays.copyOfRange(decoded, 0, decoded.length - 4);
            byte[] checksum = Arrays.copyOfRange(decoded, decoded.length - 4, decoded.length);
            byte[] actualChecksum = Arrays.copyOfRange(Sha256Hash.hashTwice(data), 0, 4);
            if (!Arrays.equals(checksum, actualChecksum)) {
                throw new AddressFormatException("Checksum does not validate");
            } else {
                return data;
            }
        }
    }

    private static byte divmod(byte[] number, int firstDigit, int base, int divisor) {
        int remainder = 0;

        for(int i = firstDigit; i < number.length; ++i) {
            int digit = number[i] & 255;
            int temp = remainder * base + digit;
            number[i] = (byte)(temp / divisor);
            remainder = temp % divisor;
        }

        return (byte)remainder;
    }

    static {
        ENCODED_ZERO = ALPHABET[0];
        INDEXES = new int[128];
        Arrays.fill(INDEXES, -1);

        for(int i = 0; i < ALPHABET.length; INDEXES[ALPHABET[i]] = i++) {
            ;
        }

    }
}
相關文章
相關標籤/搜索
本站公眾號
   歡迎關注本站公眾號,獲取更多信息