以太坊go-ethereum签名部分源码解析
以太坊go-ethereum签名部分源码解析
golang标准库里的crypto/ecdsa椭圆曲线加密算法所提供的函数有:
ecdsa.PublicKey结构体通过持有一个elliptic,Curve接口的实现体,可以提供椭圆曲线的所有属性,和相关操作;PublicKey的成员(X,Y),对应于算法理论中公钥的坐标。
func GenerateKey(c elliptic.Curve, rand io.Reader) (*PrivateKey, error)
elliptic.Curve接口声明了椭圆曲线的相关操作方法,其中Add()方法就是椭圆曲线点倍积中的“点相加”操作,Double()就是点倍积中的“点翻倍”操作,ScalarMult()根本就是一个点倍积运算(参数k是标量),IsOnCurve()检查参数所代表的点是否在该椭圆曲线上;
elliptic.CurveParams结构体实现了curve接口的所有方法,另外用成员属性定义了一个具体的椭圆曲线,比如(Gx, Gy) 表示该椭圆曲线的基点,即算法理论中的G点; N 是与基点对应的可倍积阶数n;B是椭圆曲线几何方程中的参数b,注意此处ecdsa代码包中隐含的椭圆曲线方程为y^2 = x^3 - 3x + b,故只需一项参数b即可。
ecdsa.PrivateKey是暴露给外部使用的主要结构体类型,它其实是算法理论中的私钥和公钥的集合。它的成员D,才真正对应于算法理论中的(标量)私钥。
ecdsa.ecdsaSignature对应于生成的数字签名(r, s)。
// PublicKey represents an ECDSA public key.两个big.Int类型
type PublicKey struct {
elliptic.Curve
X, Y *big.Int
} // PrivateKey represents a ECDSA private key.
type PrivateKey struct {
PublicKey
D *big.Int
} type ecdsaSignature struct {
R, S *big.Int
}
//@Time : 2018/3/23 11:33
//@Author: Greg Li
package main
import (
"encoding/hex"
"fmt"
"github.com/ethereum/go-ethereum/crypto"
)
func main() {
// 创建账户
key, err := crypto.GenerateKey()
if err !=nil {
fmt.Println(err)
}
// 私钥:64个字符
privateKey := hex.EncodeToString(key.D.Bytes())
fmt.Println(privateKey)
// 得到地址:42个字符
address := crypto.PubkeyToAddress(key.PublicKey).Hex()
fmt.Println(address)
}
/*
* 公钥:在secp256k1规范下,由私钥和规范中指定的生成点计算出的坐标(x, y)
* 非压缩格式公钥: [前缀0x04] + x + y (65字节)
* 压缩格式公钥:[前缀0x02或0x03] + x ,其中前缀取决于 y 的符号
*/
//生成公钥: 输入的私钥应当是buffer
从私钥获取有三个主要步骤 - >地址:
创建一个随机私钥(64(十六进制)字符/ 256位/ 32字节)
从该私钥导出公钥(128(十六进制)字符/ 512位/ 64字节)
从这个公钥导出地址。 (40(十六进制)字符/ 160位/ 20字节)
尽管很多人称这个地址为公钥,但其实在Ethereum中并非如此。有一个独立的公钥,作为一个中间人,你永远不会看到,除非你去寻找一个预售钱包JSON文件。
1.生成私钥
私钥是64个十六进制字符。假设每一个64字节的字符串都是一个以太坊私钥将访问一个帐户。如果计划生成一个新帐户,应确保这些帐户使用适当的RNG进行播种。
2.私钥 - >公钥
椭圆曲线数字签名算法(ECDSA)会得到一个64字节的公钥。
3.公钥 - >地址
从公钥开始(128个字符/ 64个字节)
采用公钥的Keccak-256散列。你现在应该有一个64字符/ 32字节的字符串。 (注意:SHA3-256最终成为标准,但以太坊使用Keccak)
取这个公钥的最后40个字符/ 20个字节(Keccak-256)。或换句话说,删除前24个字符/ 12个字节。这40个字符/ 20字节是地址。当前缀为0x时,它变为42个字符长。
定义
地址:以太坊地址代表一个帐户。对于EOA,地址是作为控制该账户的公钥的最后20个字节导出的,例如`cd2a3d9f938e13cd947ec05abc7fe734df8dd826。这是一个十六进制格式(基数为16的表示法),通常通过向地址附加0x来明确指出。 Web3.js和控制台函数接受有或没有这个前缀的地址,但为了透明,我们鼓励他们使用。由于地址的每个字节都用2个十六进制字符表示,所以前缀地址长度为42个字符。几个应用程序和API也是为了实现从0.5.0版本开始在Mist Ethereum钱包中引入的新校验和启用地址方案。
私钥:在[1,secp256k1n - 1]范围内随机选择的正整数(表示为长尾为32的字节数组)。
//@Time : 2018/3/23 14:47
//@Author: Greg Li
package main
import (
"fmt"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
)
func main() {
fmt.Println(verifySig(
"0x829814B6E4dfeC4b703F2c6fDba28F1724094D11",
"0x53edb561b0c1719e46e1e6bbbd3d82ff798762a66d0282a9adf47a114e32cbc600c248c247ee1f0fb3a6136a05f0b776db4ac82180442d3a80f3d67dde8290811c",
[]byte("hello"),
))
}
func verifySig(from, sigHex string, msg []byte) bool {
fromAddr := common.HexToAddress(from)
sig := hexutil.MustDecode(sigHex)
if sig[64] != 27 && sig[64] != 28 {
return false
}
sig[64] -= 27
pubKey, err := crypto.SigToPub(signHash(msg), sig)
if err != nil {
return false
}
recoveredAddr := crypto.PubkeyToAddress(*pubKey)
return fromAddr == recoveredAddr
}
func signHash(data []byte) []byte {
msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
return crypto.Keccak256([]byte(msg))
}
Ecrecover可以从签名中恢复公钥
// Ecrecover returns the uncompressed public key that created the given signature.
func Ecrecover(hash, sig []byte) ([]byte, error) {
return secp256k1.RecoverPubkey(hash, sig)
}
// SigToPub returns the public key that created the given signature.
func SigToPub(hash, sig []byte) (*ecdsa.PublicKey, error) {
s, err := Ecrecover(hash, sig)
if err != nil {
return nil, err
}
x, y := elliptic.Unmarshal(S256(), s)
return &ecdsa.PublicKey{Curve: S256(), X: x, Y: y}, nil
}
这里模拟发送一笔交易
//@Time : 2018/3/23 11:41
//@Author: Greg Li
package main
import (
"fmt"
"math/big"
"context"
"io/ioutil"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
const (
KEYJSON_FILEDIR = `/home/greg/.ethereum/keystore/UTC--2018-03-01T06-46-59.670564333Z--99853a4cc9257df5a7e1e276d2e44567f2172db1`
SIGN_PASSPHRASE = `test`
KEYSTORE_DIR = `UTC--2018-03-01T06-46-59.670564333Z--99853a4cc9257df5a7e1e276d2e44567f2172db1`
COINBASE_ADDR_HEX = `0x99853a4cc9257df5a7e1e276d2e44567f2172db1`
ALTER_ADDR_HEX = `0x0152b5c8a375bbc305a6f285c4c26d25935d5d94`
CHAIN_ID = 0
)
func main() {
// 初始化keystore
ks := keystore.NewKeyStore(
KEYSTORE_DIR,
keystore.LightScryptN,
keystore.LightScryptP)
fmt.Println()
// 创建账户
fromAccDef := accounts.Account{
Address: common.HexToAddress(COINBASE_ADDR_HEX),
}
toAccDef := accounts.Account{
Address: common.HexToAddress(ALTER_ADDR_HEX),
}
// 查找将给定的帐户解析为密钥库中的唯一条目:找到签名的账户
signAcc, err := ks.Find(fromAccDef)
if err != nil {
fmt.Println("account keystore find error:")
panic(err)
}
fmt.Printf("account found: signAcc.addr=%s; signAcc.url=%s\n", signAcc.Address.String(), signAcc.URL)
fmt.Println()
// 解锁签名的账户
errUnlock := ks.Unlock(signAcc, SIGN_PASSPHRASE)
if errUnlock != nil {
fmt.Println("account unlock error:")
panic(err)
}
fmt.Printf("account unlocked: signAcc.addr=%s; signAcc.url=%s\n", signAcc.Address.String(), signAcc.URL)
fmt.Println()
// 建立交易
tx := types.NewTransaction(
0x0,
toAccDef.Address,
new(big.Int),
0,
new(big.Int),
[]byte(`cooldatahere`))
// 打开账户私钥文件
keyJson, readErr := ioutil.ReadFile(KEYJSON_FILEDIR)
if readErr != nil {
fmt.Println("key json read error:")
panic(readErr)
}
// 解析私钥文件
keyWrapper, keyErr := keystore.DecryptKey(keyJson, SIGN_PASSPHRASE)
if keyErr != nil {
fmt.Println("key decrypt error:")
panic(keyErr)
}
fmt.Printf("key extracted: addr=%s", keyWrapper.Address.String())
// Define signer and chain id
// chainID := big.NewInt(CHAIN_ID)
// signer := types.NewEIP155Signer(chainID)
signer := types.HomesteadSigner{}
//用私钥签署交易签名
signature, signatureErr := crypto.Sign(tx.Hash().Bytes(), keyWrapper.PrivateKey)
if signatureErr != nil {
fmt.Println("signature create error:")
panic(signatureErr)
}
signedTx, signErr := tx.WithSignature(signer, signature)
if signErr != nil {
fmt.Println("signer with signature error:")
panic(signErr)
}
//连接客户端
client, err := ethclient.Dial("http://localhost:8000") // 8000=geth RPC port
if err != nil {
fmt.Println("client connection error:")
panic(err)
}
fmt.Println("client connected")
fmt.Println()
//发送交易到网络
txErr := client.SendTransaction(context.Background(), signedTx)
if txErr != nil {
fmt.Println("send tx error:")
panic(txErr)
}
fmt.Printf("send success tx.hash=%s\n", signedTx.Hash().String())
}
以太坊go-ethereum签名部分源码解析的更多相关文章
- [中文] 以太坊(Ethereum )白皮书
以太坊(Ethereum ):下一代智能合约和去中心化应用平台 翻译|巨蟹 .少平 译者注|中文读者可以到以太坊爱好者社区(www.ethfans.org)获取最新的以太坊信息. 当中本聪在2009年 ...
- 以太坊客户端Ethereum Wallet与Geth区别简介
以太坊客户端Ethereum Wallet与Geth区别简介 最近有不少朋友在搭建交易平台,在咨询和技术交流的过程中发现很多朋友不太清楚Ethereum Wallet和Geth区别.甚至有朋友使用Ge ...
- 阿里云视频直播API签名机制源码
阿里云视频直播API签名机制源码 本文展示:通过代码实现下阿里视频直播签名处理规则 阿里云视频直播签名机制,官方文档链接:https://help.aliyun.com/document_detail ...
- bitcoin 源码解析 - 交易 Transaction(三) - Script
bitcoin 源码解析 - 交易 Transaction(三) - Script 之前的章节已经比较粗略的解释了在Transaction体系当中的整体运作原理.接下来的章节会对这个体系进行分解,比较 ...
- 【原】Android热更新开源项目Tinker源码解析系列之一:Dex热更新
[原]Android热更新开源项目Tinker源码解析系列之一:Dex热更新 Tinker是微信的第一个开源项目,主要用于安卓应用bug的热修复和功能的迭代. Tinker github地址:http ...
- EventBus源码解析 源码阅读记录
EventBus源码阅读记录 repo地址: greenrobot/EventBus EventBus的构造 双重加锁的单例. static volatile EventBus defaultInst ...
- 【转】Java HashMap 源码解析(好文章)
.fluid-width-video-wrapper { width: 100%; position: relative; padding: 0; } .fluid-width-video-wra ...
- SpringMVC源码解析- HandlerAdapter - ModelFactory(转)
ModelFactory主要是两个职责: 1. 初始化model 2. 处理器执行后将modle中相应参数设置到SessionAttributes中 我们来看看具体的处理逻辑(直接充当分析目录): 1 ...
- RTKLIB源码解析(一)——单点定位(pntpos.c)
RTKLIB源码解析(一)--单点定位(pntpos.c) 标签: GNSS RTKLIB 单点定位 [TOC] pntpos int pntpos (const obsd_t *obs, int n ...
随机推荐
- 笔记︱图像语义分割(FCN、CRF、MRF)、论文延伸(Pixel Objectness、)
图像语义分割的意思就是机器自动分割并识别出图像中的内容,我的理解是抠图- 之前在Faster R-CNN中借用了RPN(region proposal network)选择候选框,但是仅仅是候选框,那 ...
- UBOOT添加命令的执行流程
BootLoader(引导装载程序)是嵌入式系统软件开发的第一个环节,它把操作系统和硬件平台衔接在一起,对于嵌入式系统的后续软件开发十分重要,在整个开发中也占有相当大的比例.U-BOOT是当前比较流行 ...
- Oracle SQL Developer 连接数据库如何对应数据库配置文件
Oracle SQL Developer 连接数据库如何对应数据库配置文件 1.数据库配置文件 hibernate.connection.url jdbc:oracle:thin:@146.56.35 ...
- PCI-E配置MSI中断流程解析
在传统的pci中断体系中,每一个pci总线上的设备被分配一个特定的中断号,然后当设备需要中断cpu时,设备直接发出int信号,然后在cpu的inta引脚拉低的时候将自己的中断号放在数据总线上,一切都要 ...
- Python中ValueError: invalid literal for int() with base 10 的实用解决办法
爬虫代理IP由芝麻HTTP服务供应商提供今天在写爬虫程序的时候由于要翻页,做除法分页的时候出现了 totalCount = ' totalPage = int(totalCount)/20 Value ...
- SQLite笔记
一.SQLite下载: http://www.sqlite.org/download.html (或在NuGet下载安装) 二.SQLite操作: 1.添加引用System.Data.SQLite,如 ...
- 深度优先搜索DFS和广度优先搜索BFS简单解析(新手向)
深度优先搜索DFS和广度优先搜索BFS简单解析 与树的遍历类似,图的遍历要求从某一点出发,每个点仅被访问一次,这个过程就是图的遍历.图的遍历常用的有深度优先搜索和广度优先搜索,这两者对于有向图和无向图 ...
- windows打开和关闭默认共享方法汇总
Windows启动时都会默认打开admin$ ipc$ 和每个盘符的共享,对于不必要的默认共享,一般都会把它取消掉,可当又需要打开此默认共享时,又该从哪里设置呢.经过自己的验证,汇总出一下方法. 一: ...
- MySql获取所有表名
如何获取MySql中所有表的的表名? sql语句是:show tables 返回结果如下: 不仅仅返回了所有的表名,更返回了视图的名字.
- poj1417菜鸡的详细题解(希望能帮助到和我一样陷于本题的新手)
题意太麻烦就偷懒转载他人的题意..... 题意转载自http://blog.csdn.net/acm_cxlove/article/details/7854526 by---cxlove ...