Bitcoinj源码阅读笔记一

bitcoinj core篇

Monetary.java

这是一个接口类,实现此接口代表是一个货币,例如比特币。接口中包含int smallestUnitExponet()(最小分割单元,通常是8)、long getValue()int sigum()

Coin.java

这是一个实现了Monetary接口的final类。 定义了不同大小的比特币:ZEROCENTMILLICOINMICROCOINSATOSHIFIFTY_COINNEGATIVE_SATOSHI。定义了价值的加减乘除运算方法。

Base58.java

这个类主要用于编码比特币地址或者任意数据,使其变为方便阅读的有大小写字母组成的字符串。bitcoin中的base58与Flickr的略有不同。校验部分使用sha256哈希两次。主要的方法有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//58字符 不包括0IlO
public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
private static final char ENCODED_ZERO = ALPHABET[0];
private static final int[] INDEXES = new int[128];
static {
Arrays.fill(INDEXES, -1);
for (int i = 0; i < ALPHABET.length; i++) {
INDEXES[ALPHABET[i]] = i;
}
}
/**
* 将输入的byte编码成base58 字符串,无校验和.
*
* @param input the bytes to encode
* @return base58-encoded string
*/
public static String encode(byte[] input){...}

/**
* 解码base58字符串 为原数据的 bytes.
*
* @param input the base58-encoded string to decode
* @return the decoded data bytes
* @throws AddressFormatException 如果base58不是一个合法的字符串 继承于IllegalArgumentException
*/
public static byte[] decode(String input) throws AddressFormatException {...}

VersionedChecksummedBytes.java

比特币key的格式化类。格式化一般包括[one version byte]、[data types]、[4 checksum bytes]。在base58的基础上添加了比特币地址的前后缀校验。

bitcoinj在单元测试中使用BaseEncoding类,这个类来自com.google.common.io

1
2
3
4
5
6
7
public abstract class BaseEncoding {
private static final BaseEncoding BASE64 = new BaseEncoding.StandardBaseEncoding("base64()", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/", Character.valueOf('='));
private static final BaseEncoding BASE64_URL = new BaseEncoding.StandardBaseEncoding("base64Url()", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_", Character.valueOf('='));
private static final BaseEncoding BASE32 = new BaseEncoding.StandardBaseEncoding("base32()", "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567", Character.valueOf('='));
private static final BaseEncoding BASE32_HEX = new BaseEncoding.StandardBaseEncoding("base32Hex()", "0123456789ABCDEFGHIJKLMNOPQRSTUV", Character.valueOf('='));
private static final BaseEncoding BASE16 = new BaseEncoding.StandardBaseEncoding("base16()", "0123456789ABCDEF", (Character)null);
}

从源码中可以看出其支持base16base32base32Hexbase64base64Url

Encoding Alphabet char:byte ratio Default padding Comments
base16() 0-9 A-F 2.00 N/A 传统16进制,默认大写字母
base32() A-Z 2-7 1.60 = 方便阅读,不会有0/O, 1/l的混淆,默认大写
base32Hex() 0-9 A-V 1.60 =
base64() 0-9 A-Z a-z + / 1.33 =
base64Url() 0-9 A-Z a-z - _ 1.33 = 可安全的用于文件名、URLs, 不怕泄露

Address.java

比特币种的地址类似“1MsScoe2fTJoq4ZPdQgqyhgWeoNamYPevy”是从椭圆曲线产生的公钥处生成的(详细原理在Bitcoinj学习笔记之开始使用的密钥和地址有介绍)。因为是使用RIPEMD160对公钥进行hash,所以长度是160bits即20bytes。比特币采用支付给脚本的哈希值(pay-to-script-hash,P2SH)。

比特币的工作机制要求币的发送者必须在交易时时明确指定脚本。这种机制有的时候不太适用:假如在遇到支付场景时,卖家使用了多重签名地址,需要用户支付给一个脚本地址而不是一个简单的地址,这样对于用户而言会变得很复杂。

比特币用了一种很聪明的办法来解决这个问题,不仅可以实现多重签名地址支付,而且还可以实现复杂的资金监管规则。比特币使用的办法是:收款方告诉付款方“请把比特币支付给某个脚本地址,脚本的哈希值是xxx,在取款的时候,我会提上述哈希值对应的脚本,同时,提供数据通过脚本的验证。”而不是“请把比特币支付给某个公钥,公钥的哈希值是XXX”。

类中重要的方法有:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class Address extends VersionedChecksummedBytes {
/**
* 20字节
*/
public static final int LENGTH = 20;
private transient NetworkParameters params;
/**
*
* eg. new Address(MainNetParams.get(), NetworkParameters.getAddressHeader(), Hex.decode("4a22c3c4cbb31e4d03b15550636762bda0baf85a"));</pre>
*/
public Address(NetworkParameters params, int version, byte[] hash160) throws WrongNetworkException {

}

/**
* 返回代表P2SH脚本hash的地址
*/
public static Address fromP2SHHash(NetworkParameters params, byte[] hash160) {}
/**
* 返回一个地址,根据给定的脚本公钥计算出的hash
*/
public static Address fromP2SHScript(NetworkParameters params, Script scriptPubKey) {}

}

StoredBlock.java

类中包含了blockheader、链工作总量等额外信息,提高了查询和计算效率不再需要每次从创世区块开始遍历。

1
2
3
4
5
6
7
8
9
10
11
12
13
14

public static final int CHAIN_WORK_BYTES = 12;
public static final byte[] EMPTY_BYTES = new byte[CHAIN_WORK_BYTES];
public static final int COMPACT_SERIALIZED_SIZE = Block.HEADER_SIZE + CHAIN_WORK_BYTES + 4; // for height

private Block header;
private BigInteger chainWork;
private int height;

public StoredBlock(Block header, BigInteger chainWork, int height) {
this.header = header;
this.chainWork = chainWork;
this.height = height;
}
坚持原创技术分享,记录点滴成长历程!