以太坊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 ...
随机推荐
-
C++学习网站总结(转)
总结帖: http://club.topsage.com/thread-361504-1-1.html Visual C++ (VC) / MFC 电子书下载: Visual C++ 2008 入门经 ...
-
Swift学习笔记十
枚举 一个枚举为一组相关联的值定义一个通用类型,并且让你可以在代码中类型安全地操作这些值. C中的枚举将关联的名字指派给一系列整型值.Swift中的枚举类型更为活泼,并不需要为每个成员指定值,如果指定 ...
-
滤镜简单demo(转,供参考)
NSURL *iamgeUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"default" ...
-
Ubuntu12.04LTS安装好后是空白桌面的解决步骤(更新显卡驱动)
安装完毕启动后,明显慢的要死,登陆后竟然是一个空白的桌面环境,Ctrl+Alt+T 根本没有任何反应.唯一的反应就是右键能够创建文件和文档. 同时打开的窗口没有最大化,最小化及关闭按钮. GOOGLE ...
-
Tomcat 配置文件 server.xml
Tomcat隶属于Apache基金会,是开源的轻量级Web应用服务器,使用非常广泛.server.xml是Tomcat中最重要的配置文件,server.xml的每一个元素都对应了Tomcat中的一个组 ...
-
js较深入的知识点
浏览器渲染过程是怎样的?重绘重排是什么?如何避免过多的重绘重排? 将html解析为dom树; 将css解析为cssom; 结合DOM树和CSSOM树,生成一棵渲染树(Render Tree); 生成布 ...
- jsp四大作用域
-
Idea书签管理器的使用
1. 添加书签 以光标所在的行,为落点, 方式一: F11 , 添加一个默认的书签 方式二:Crtl + Shift + 数字 , 添加一个带编号 的书签 2. 查看书签 方式一:Shift + F1 ...
-
用interrupt()中断Java线程
最近在学习Java线程相关的东西,和大家分享一下,有错误之处欢迎大家指正. 假如我们有一个任务如下,交给一个Java线程来执行,如何才能保证调用interrupt()来中断它呢? class ATas ...
-
java在window系统的一些问题
一般的, 在windows cmd上启动 utf-8编码的java应用 会有一些字符集问题 可以修改cmd的字符集编码为utf8 , chcp 65001 java -D有空格时 区别: VM参数是 ...