赞
踩
// 具体说明已经在代码中详细注释,实现HASH160、HASH256和Base58编码
// 代码如下
/* 比特币测试网地址的生成 使用RIPEMD-160、SHA-256哈希算法以及Base58编码对给定公钥生成地址 给定公钥: public key 1: 02b1ebcdbac723f7444fdfb8e83b13bd14fe679c59673a519df6a1038c07b719c6 public key 2: 036e69a3e7c303935403d5b96c47b7c4fa8a80ca569735284a91d930f0f49afa86 提示: 比特币中有两种复合式的哈希函数,分别为: HASH160,即先对输入做一次SHA256,再做一次RIPEMD160; HASH256,即先对输入做一次SHA256,再做一次SHA256。 本练习要求的version byte为0x6f(测试网)。 */ package main import ( "crypto/sha256" "encoding/hex" "fmt" "github.com/shengdoushi/base58" "golang.org/x/crypto/ripemd160" ) const version byte = 0x6f const addressChecksumLen = 4 // 定义Checksum长度为4个字节 var PublicKey1 = "02b1ebcdbac723f7444fdfb8e83b13bd14fe679c59673a519df6a1038c07b719c6" var PublicKey2 = "036e69a3e7c303935403d5b96c47b7c4fa8a80ca569735284a91d930f0f49afa86" func Hash160(publicKey []byte) []byte { // 先对输入做一次SHA256 hash256 := sha256.New() hash256.Write(publicKey) hash := hash256.Sum(nil) // hash256得到的结果hash // 再做一次RIPEMD160 ripemd160 := ripemd160.New() ripemd160.Write(hash) // 对hash再ripemd160计算得到的结果 return ripemd160.Sum(nil) } // HASH256,返回CheckSum变量 func CheckSum(payload []byte) []byte { hash1 := sha256.Sum256(payload) // HASH256,即先对输入做一次SHA256 hash2 := sha256.Sum256(hash1[:]) // 再做一次SHA256。 return hash2[:addressChecksumLen] // 切片取前4字节 } // 返回Address func GetAddress(PublicKey string) string { pubkey, _ := hex.DecodeString(PublicKey) // 返回数组 ripemd160Hash := Hash160((pubkey)) // HASH160计算,得到Fingerprint version_hash := append([]byte{version}, ripemd160Hash...) // 将version byte加到Fingerprint前面 checkbytes := CheckSum(version_hash) // 将version_hash即version byte+Fingerprint进行HASH256后取前4字节,得到Checksum bytes := append(version_hash, checkbytes...) // 将version_hash即version byte+Fingerprint,添加到Checksum前面,得到拼接结果 // 进行base58编码 myAlphabet := base58.BitcoinAlphabet // 编码表 var encodstring string = base58.Encode(bytes, myAlphabet) return encodstring // 得到最终结果 } func main() { address1 := GetAddress(PublicKey1) address2 := GetAddress(PublicKey2) fmt.Printf("address1:%s\n", address1) fmt.Printf("assress2:%s\n", address2) }
//运行截图如下
求得结果,地址是合法符合要求的
// 具体说明已经在代码中详细注释
// 代码如下
/* 请用Go语言实现一棵叶子节点数为16的Merkle Tree,并在叶子节点存储任意字符 串,并在所有非叶节点计算相应Hash值 请将上一步生成的Merkle Tree任一叶子节点数据进行更改,并重新生成其余Hash值。利 用Merkle Tree的特点对该修改位置进行快速定位 即设计函数func compareMerkleTree(*MTree tree1, *MTree tree2) (int index) { } */ package main import ( "bytes" "crypto/sha256" "fmt" ) // 节点结构体定义 type MerkleNode struct { Left *MerkleNode // 左孩子 Right *MerkleNode // 右孩子 Data []byte // 节点存储的数据,切片类型 } // 树的结构体定义 type MerkleTree struct { RootNode *MerkleNode // 根节点 } // 创建节点 func NewMerkleNode(left, right *MerkleNode, data []byte) *MerkleNode { mNode := MerkleNode{} // 创建一个空节点 // 如果左右子树为空,那么data就是叶子节点 if left == nil && right == nil { // 计算哈希sha256 hash := sha256.Sum256(data) mNode.Data = hash[:] // 以切片的类型,数据给Data } else { // 有孩子的情况,非叶子节点存储该节点两个孩子节点的Hash值 prevHash := append(left.Data, right.Data...) // 将左右子树的数据集合在一起 // 计算哈希sha256 hash := sha256.Sum256(prevHash) mNode.Data = hash[:] // 以切片的类型,数据给Data } // 给mNode节点的左右子树赋值 mNode.Left = left mNode.Right = right return &mNode // 返回mNode节点地址 } // 对最开始的数据初始化,将输入的16个字符串转化为节点,以便于新建树 func initial_datas(data [][]byte) []MerkleNode { // 输入二维数组,返回[]MerkleNode数组 var nodes []MerkleNode for _, datum := range data { // 得到字符串数据 node := NewMerkleNode(nil, nil, datum) // 左右孩子空,将datum数据传入进去,返回一个地址 nodes = append(nodes, *node) // 取这个node的值,加到nodes数组里面去 } return nodes // 返回nodes数组 } // 新建Merkle Tree func NewMerkleTree(data [][]byte) *MerkleTree { // 输入二维数组,返回*MerkleTree这一棵树 var nodes []MerkleNode // 构造节点 for _, datum := range data { // 得到字符串数据 node := NewMerkleNode(nil, nil, datum) // 左右孩子空,将datum数据传入进去,返回一个地址 nodes = append(nodes, *node) // 取这个node的值,加到nodes数组里面去 } // 层层计算,这里树共5层,16个叶子节点 for i := 0; i < 5; i++ { // 循环五层 var newLevel []MerkleNode // 每一层的节点数组 //相邻两个节点合并 for j := 0; j < len(nodes); j += 2 { // 对每一层循环 node := NewMerkleNode(&nodes[j], &nodes[j+1], nil) // j=0:0与1形成父节点,j=2:2与3形成父节点,j=4:4与5形成父节点... newLevel = append(newLevel, *node) // 加入newLevel数组 } nodes = newLevel if len(nodes) == 1 { // 1说明已经是根节点了 break } } mTree := MerkleTree{&nodes[0]} // 根节点即是第一个节点 return &mTree // 返回根节点地址 } // 比较修改后的前后两棵树,对该修改位置进行快速定位 func compareMerkleTree(tree1, tree2 *MerkleTree, initial_data []MerkleNode) int { //返回index索引 comparenode1 := (*tree1).RootNode // 第一棵树的根节点 comparenode2 := (*tree2).RootNode // 第二棵树的根节点 var key_val []byte // 该切片与修改某个叶子节点数据后的树(第二棵树)进行对比,来找到修改的位置的下标 // 开始遍历查找被修改的数据 for j := 0; j < 5; j++ { // 无左右孩子,已经到达叶子节点 if (*comparenode2).Left == nil && (*comparenode2).Right == nil { key_val = (*comparenode2).Data // 叶子节点值赋给key_val,也就是找到了被修改的叶子节点 break } // 如果comparenode1与comparenode2两棵树相同位置的节点,其数据hash值不同,则找到被修改值的路径 if !(bytes.Equal((*comparenode1).Data, (*comparenode2).Data)) { // 假设由于这两个节点,左孩子被修改,导致这两个节点不同 tmp_comparenode1 := comparenode1.Left tmp_comparenode2 := comparenode2.Left if !bytes.Equal((*tmp_comparenode1).Data, (*tmp_comparenode2).Data) { // 确实是由于左孩子的问题,继续在左孩子的路径上找下去 comparenode1 = comparenode1.Left comparenode2 = comparenode2.Left } else { // 是由于右孩子的问题,则继续在右孩子的路径上找下去 comparenode1 = comparenode1.Right comparenode2 = comparenode2.Right } } } // 对被修改的叶子节点进行定位 var i int for i = 0; i < len(initial_data); i++ { // 找到的叶子节点key_val与最开始初始化并且进行修改数据之后的数组对比,找到key_val在initial_data数组中的下标 if bytes.Equal(key_val, initial_data[i].Data) { break } } return i + 1 // 返回被修改数据下标+1,也就是第几个数据 } func main() { datas := [][]byte{ // 定义二维数组 []byte("node1"), []byte("node2"), []byte("node3"), []byte("node4"), []byte("node5"), []byte("node6"), []byte("node7"), []byte("node8"), []byte("node9"), []byte("node10"), []byte("node11"), []byte("node12"), []byte("node13"), []byte("node14"), []byte("node15"), []byte("node16"), } tree1 := NewMerkleTree(datas) // 原始的第一棵树 datas[3] = []byte("node100") // 修改第4个数据 tree2 := NewMerkleTree(datas) // 修改后的第二棵树 ini_datas := initial_datas(datas) // 数据初始化 index := compareMerkleTree(tree1, tree2, ini_datas) // 查找到的位置 fmt.Printf("已找到到被修改的叶子节点位置,位置是:%d", index) // 打印输出 }
//运行截图如下
//求得结果,找到了预计的被修改数据的位置
// 具体说明已经在代码中详细注释
// 代码如下
/* 靓号,泛指阿拉伯数字组成的连续相同的易于记忆的号码。车牌靓号多为四个连号或 8899、5566之类的号码。在本实验的比特币地址中,由于采用了Base58编码,所以可能存在 连续出现3个拉丁字母的“靓号地址”。 请完成以下实验: 使用Go语言编写一段程序,程序的输出为一个合法的比特币测试网地址(version byte 为0x6f),且要求: 1. 地址中包含3个连续的小写字母c。 2. 生成公钥时,使用安全的随机数生成器crpyto/rand。 使用浏览器访问任意水龙头网站,输入刚刚生成的地址,获取小额的测 试用比特币,记录下交易ID。 如果领取成功,网页将显示如下交易信息。 */ package main import ( "crypto/rand" "crypto/sha256" "fmt" "github.com/shengdoushi/base58" "golang.org/x/crypto/ripemd160" ) const version byte = 0x6f // version byte为0x6f const addressChecksumLen = 4 // 定义Checksum长度为4个字节 func Hash160(publicKey []byte) []byte { // 先对输入做一次SHA256 hash256 := sha256.New() hash256.Write(publicKey) hash := hash256.Sum(nil) // hash256得到的结果hash // 再做一次RIPEMD160 ripemd160 := ripemd160.New() ripemd160.Write(hash) // 对hash再ripemd160计算得到的结果 return ripemd160.Sum(nil) } // HASH256,返回CheckSum变量 func CheckSum(payload []byte) []byte { hash1 := sha256.Sum256(payload) // HASH256,即先对输入做一次SHA256 hash2 := sha256.Sum256(hash1[:]) // 再做一次SHA256。 return hash2[:addressChecksumLen] // 切片取前4字节 } // 返回Address func GetAddress(pubkey []byte) string { ripemd160Hash := Hash160((pubkey)) // HASH160计算,得到Fingerprint version_hash := append([]byte{version}, ripemd160Hash...) // 将version byte加到Fingerprint前面 checkbytes := CheckSum(version_hash) // 将version_hash即version byte+Fingerprint进行HASH256后取前4字节,得到Checksum bytes := append(version_hash, checkbytes...) // 将version_hash即version byte+Fingerprint,添加到Checksum前面,得到拼接结果 // 进行base58编码 myAlphabet := base58.BitcoinAlphabet // 编码表 var encodstring string = base58.Encode(bytes, myAlphabet) return encodstring // 得到最终结果 } func main() { // 生成随机的 20 字节公钥 pubKey := make([]byte, 20) rand.Read(pubKey) // 生成公钥时,使用安全的随机数生成器crpyto/rand bAddress := GetAddress(pubKey) // 遍历查询,是否有连续三个c,也就是“靓号地址”中包含3个连续的小写字母c for i := 0; i <= len(bAddress)-3; i++ { if bAddress[i] == 'c' && bAddress[i+1] == 'c' && bAddress[i+2] == 'c' { // 连续三个c fmt.Printf("已得到比特币测试网“靓号地址”,是:%s\n", bAddress) return } } // 如果没有找到满足条件的地址,则重新生成一个地址,重新查找 main() }
//关键部分如下截图:
//运行截图如下
使用浏览器访问任意水龙头网站,输入刚刚生成的地址,获取小额的测
试用比特币
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。