以太坊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 ...
随机推荐
- 自动化测试 selenium中WebDriver 和WebElement的区别
之前在做UI自动化测试中 经常会用到 WebDriver 和 WebElement两个类 以前对这两个类总感觉有点傻傻分不清 昨天看了下之前下的代码 结合自己的理解 对着两个类的区别和关系 ...
- Caffe、TensorFlow、MXnet三个开源库对比
库名称 开发语言 支持接口 安装难度(ubuntu) 文档风格 示例 支持模型 上手难易 Caffe c++/cuda c++/python/matlab *** * *** CNN ** MXNet ...
- 最新的Android版本和API Level的对应关系表
在项目开发过程中,经常会用到API Level和Android平台版本的对照,来进行一些方法的调用,现在就把对照表贴出来,供开发人员参考,并且方便自己查阅. Platform Version API ...
- PHP openssl函数库
php openssl 函数库中.提供了大量的函数.但是我们一般用的最多的,就是 openssl_encrypt string openssl_encrypt ( string $data , str ...
- Java Web项目(Extjs)报错三
1. Java Web项目(Extjs)报错三 具体报错如下: at org.jbpm.pvm.internal.processengine.SpringHelper.createProcessEng ...
- 做一个合格的Team Leader -- 基本概念
1.领导和管理 人们乐于被领导:他们不喜欢被管理,不喜欢像牛一样被驱赶或指挥. 管理者强迫人们服从他们的命令,而领导者则会带领他们一起工作. 管理是客观的,没有个人感情因素,它假定被管理者没有思想和感 ...
- Caused by: java.lang.ClassNotFoundException: org.objectweb.asm.commons.EmptyVisitor
1.错误描述 2014-7-13 1:45:53 org.apache.struts2.spring.StrutsSpringObjectFactory info 信息: ... initialize ...
- 【BZOJ2875】随机数生成器(矩阵快速幂)
[BZOJ2875]随机数生成器(矩阵快速幂) 题面 Description 栋栋最近迷上了随机算法,而随机数是生成随机算法的基础.栋栋准备使用线性同余法(Linear Congruential Me ...
- 【BZOJ1207】【HNOI2004】打鼹鼠(动态规划)
[BZOJ1207][HNOI2004]打鼹鼠 题面 BZOJ题面 题解 考虑到m的范围只有10000 O(m^2)的复杂度是可以接受的 所以直接暴力DP 每次枚举前面出现的鼹鼠 检查是否能够转移过来 ...
- NTT中可用素数模数原根表
常用素数: P = 1004535809 ====> pr = 3 P = 998244353 =====> pr = 3 //(g 是mod(r*2^k+1)的原根) 素数 r k g ...