
华特东方--地址生成
Hello , 我是09
此篇内容为我2018~2020年在【库神】工作时,对部分重要知识做的学习总结
随着比特币冲破10万美金,行业又再次兴起,希望相关从业者可以学习到你需要的内容
前言
华特东方采用Secp256k1算法。
华特东方根据分层确定性推算地址的方法和比特系列完全一致,也就是说可以和比特币一样根据扩展公钥来推出地址。
华特东方根据公钥推导出地址的方法和BCH的新地址,以及BTM的地址推算及其相似。
华特东方的地址前缀“htdf1”是最后得到地址后,手动拼接在最前面的。(并不是像其他的币种通过一个固定的前缀version编码后得到)
地址生成
先带大家看一下地址生成的方法:
+ (NSString )p_GetHTDFAddressWithPubKey:(NSData )publicKey slip44Code:(uint32_t)slip44_code
{
// 1.首先是根据公钥直接生成control_programe
NSData *publickey_hash160 = publicKey.SDK_SHA256_RMD160;
// 2. 再将数据转换为字符串
NSString *publickey_hash160_str = [NSString hexWithData:publickey_hash160];
// 3. 再将control_programe转十进制Bytes数组
NSMutableArray *bytes_array = [NSMutableArray arrayWithArray:publickey_hash160_str.hexStringToDecimalBytesArray];
// 4. 移位转换
NSArray *converted_array = [self o_GetPayLoadWithArray:bytes_array frombits:8 tobits:5 pad:YES];
// 5. 获取有效载荷
NSArray *payload_array = [self o_Bech32_Create_ChecksumWithPrefix:@"htdf" payload:converted_array];
// 6. 移位转换拼接有效载荷
NSArray *checksum = [converted_array arrayByAddingObjectsFromArray:payload_array];
// 7. bech32转码
NSString *bech_encode_str = [self p_GetBech32StrWithArray:checksum];
// 8. 拼接前缀并返回
return [NSString stringWithFormat:@"htdf1%@",bech_encode_str];
}
了解币种较多的伙伴可能会比较熟悉,这和BTM以及BCH的carsh地址生成方式很相似。几个关键点如下:
华特东方对公钥进行hash采用的是hash160)。(SHA256 + RMD16)
十六进制字符串转十进制数组。
移位,这个地方要注意是将8个bit变为5个bit,这个地方下面我会细讲。
有效载荷,这个大家可以理解为比特系列地址的4个byte的校验码,唯独不同的它是6个元素的数组。
十六进制字符串转十进制数组实现如下:
- (NSArray *)hexStringToDecimalBytesArray
{
int j=0;
Byte bytes[128]; ///3ds key的Byte 数组, 128位
NSMutableArray *byteArray = [NSMutableArray array];
for(int i=0;i<[self length];i++)
{
int int_ch; /// 两位16进制数转化后的10进制数
unichar hex_char1 = [self characterAtIndex:i]; ////两位16进制数中的第一位(高位*16)
int int_ch1;
if(hex_char1 >= '0' && hex_char1 <='9')
int_ch1 = (hex_char1-48)*16; //// 0 的Ascll - 48
else if(hex_char1 >= 'A' && hex_char1 <='F')
int_ch1 = (hex_char1-55)*16; //// A 的Ascll - 65
else
int_ch1 = (hex_char1-87)*16; //// a 的Ascll - 97
i++;
unichar hex_char2 = [self characterAtIndex:i]; ///两位16进制数中的第二位(低位)
int int_ch2;
if(hex_char2 >= '0' && hex_char2 <='9')
int_ch2 = (hex_char2-48); //// 0 的Ascll - 48
else if(hex_char1 >= 'A' && hex_char1 <='F')
int_ch2 = hex_char2-55; //// A 的Ascll - 65
else
int_ch2 = hex_char2-87; //// a 的Ascll - 97
int_ch = int_ch1+int_ch2;
[byteArray addObject:[NSString stringWithFormat:@"%d",int_ch]];
bytes[j] = int_ch; ///将转化后的数放入Byte数组里
j++;
}
return byteArray;
}
移位:大家应该知道 1个Byte = 8个bit,这个方法就是将8个bit转换为5个bit,那么原来的数据转换后就会变长。例如:20个Byte,每个Byte变为5个bit后就是32个Byte。方法如下:
+ (NSArray )o_GetPayLoadWithArray:(NSArray )array frombits:(int)frombits tobits:(int)tobits pad:(BOOL)pad
{
//General power-of-2 base conversion.
int acc = 0;
int bits = 0;
NSMutableArray *ret = [NSMutableArray array];
int maxv = (1 << tobits) - 1;
int max_acc = (1 << (frombits + tobits - 1)) - 1;
for (NSString *byte in array) {
int value = [byte intValue];
if (value < 0 || (value >> frombits)){
return nil;
}
acc = ((acc << frombits) | value) & max_acc;
bits += frombits;
while (bits >= tobits) {
bits -= tobits;
int know = (acc >> bits) & maxv;
[ret addObject:[NSString stringWithFormat:@"%d",know]];
}
}
if (pad) {
if (bits){
int know = (acc << (tobits - bits)) & maxv;
[ret addObject:[NSString stringWithFormat:@"%d",know]];
}
}
else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)){
return nil;
}
return ret;
}
获取有效载荷,其实就是华特东方地址的校验码,代码如下:
+ (NSArray )o_Bech32_Create_ChecksumWithPrefix:(NSString )prefix payload:(NSArray *)payLoadArray
{
payLoadArray = [[self p_Bech32_Prefix_Expand:prefix] arrayByAddingObjectsFromArray:payLoadArray];
payLoadArray = [payLoadArray arrayByAddingObjectsFromArray:@[@"0",@"0",@"0",@"0",@"0",@"0"]];
long long poly = [self p_Bech32_PolymodWithValue:payLoadArray];
NSMutableArray *tempArray = [NSMutableArray array];
for (int i = 0; i < 6; i++) {
long long num = ((poly >> 5 * (5 - i)) & 31);
[tempArray addObject:[NSString stringWithFormat:@"%lld",num]];
}
return tempArray;
}
+ (NSArray )p_Bech32_Prefix_Expand:(NSString )prefix
{
NSString *tempStr;
NSMutableArray *tempArray = [NSMutableArray array];
for (int i = 0; i < [prefix length]; i++) {
tempStr = [prefix substringWithRange:NSMakeRange(i, 1)];
int code = [tempStr characterAtIndex:0];
code = code >> 5;
[tempArray addObject:[NSString stringWithFormat:@"%d",code]];
}
tempArray = [NSMutableArray arrayWithArray:[tempArray arrayByAddingObjectsFromArray:@[@"0"]]];
for (int i = 0; i < [prefix length]; i++) {
tempStr = [prefix substringWithRange:NSMakeRange(i, 1)];
int code = [tempStr characterAtIndex:0];
code = code & 31;
[tempArray addObject:[NSString stringWithFormat:@"%d",code]];
}
return tempArray;
}
// 聚模运算
+ (long int)p_Bech32_PolymodWithValue:(NSArray *)values
{
long long chk = 1;
NSArray *generator = @[@(0x3b6a57b2),@(0x26508e6d),@(0x1ea119fa),@(0x3d4233dd),@(0x2a1462b3)];
for (NSString *value in values){
long long top = chk >> 25;
chk = ((chk & 0x1ffffff) << 5) ^ [value longLongValue];
for (int i = 0; i < generator.count; i++) {
if ((top >> i) & 1) {
chk ^= [generator[i] longLongValue];
}
}
}
return chk ^ 1;
}
bech32加密方法,这个和BCH的Crash地址以及BTM是通用的,很好理解,不解释,代码如下:
+ (NSString )p_GetBech32StrWithArray:(NSArray )payLoad
{
@try{
NSString *base32Str = [NSString string];
for (NSString *code in payLoad) {
base32Str = [base32Str stringByAppendingString:[@"qpzry9x8gf2tvdw0s3jn54khce6mua7l" substringWithRange:NSMakeRange([code integerValue], 1)]];
}
return [NSString stringWithFormat:@"%@",base32Str];
}@catch (NSException *exception) {
//NSLog(@"Request the gaceUsed get an error:%@",exception);
} @finally {
}
}
数据校验
为了方便大家核对自己的数据是否正确,作者这里将每一步对应的结果PO上来提供给大家参考:
参数:
传入的公钥:0259d492265cded5c38aa5dc98bb9b9cacf0b7c1e8651efeb2bfbc4fd8545e4a05
Slip44_code : 346
+ (NSString )p_GetHTDFAddressWithPubKey:(NSData )publicKey slip44Code:(uint32_t)slip44_code
{
// 1.首先是根据公钥直接生成control_programe
NSData *publickey_hash160 = publicKey.SDK_SHA256_RMD160;
结果:<6bb01a0c 680f6c20 a9c6419e d276cc2c 8291830b>
// 2. 再将数据转换为字符串
NSString *publickey_hash160_str = [NSString hexWithData:publickey_hash160];
结果:6bb01a0c680f6c20a9c6419ed276cc2c8291830b
// 3. 再将control_programe转十进制Bytes数组
NSMutableArray *bytes_array = [NSMutableArray arrayWithArray:publickey_hash160_str.hexStringToDecimalBytesArray];
结果:[107,176,26,12,104,15,108,32,169,198,65,158,210,118,204,44,130,145,131,11]
// 4. 移位转换
NSArray *converted_array = [self o_GetPayLoadWithArray:bytes_array frombits:8 tobits:5 pad:YES];
结果:[13,14,24,1,20,3,3,8,1,29,22,2,1,10,14,6,8,6,15,13,4,29,22,12,5,18,1,9,3,0,24,11]
// 5. 获取有效载荷
NSArray *payload_array = [self o_Bech32_Create_ChecksumWithPrefix:@"htdf" payload:converted_array];
结果:[14,23,24,21,12,15]
// 6. 移位转换拼接有效载荷
NSArray *checksum = [converted_array arrayByAddingObjectsFromArray:payload_array];
结果:[13,14,24,1,20,3,3,8,1,29,22,2,1,10,14,6,8,6,15,13,4,29,22,12,5,18,1,9,3,0,24,11,14,23,24,21,12,15]
// 7. bech32转码
NSString *bech_encode_str = [self p_GetBech32StrWithArray:checksum];
结果:dwcp5rrgpakzp2wxgx0dyakv9jpfrqctwhc4v0
// 8. 拼接前缀并返回
return [NSString stringWithFormat:@"htdf1%@",bech_encode_str];
结果:htdf1dwcp5rrgpakzp2wxgx0dyakv9jpfrqctwhc4v0
}
总结
看似用到的方法很多,但是仔细理解一下并不难。
真正的华特东方地址就是 htdf1 + bech32 ( 移位 ( 公钥hash ) + 有效载荷 )
本文是原创文章,采用 CC BY-NC-ND 4.0 协议,完整转载请注明来自 程序员小航
评论
匿名评论
隐私政策
你无需删除空行,直接评论以获取最佳展示效果