github.com/linapex/ethereum-dpos-chinese@v0.0.0-20190316121959-b78b3a4a1ece/signer/core/api.go (about)

     1  
     2  //<developer>
     3  //    <name>linapex 曹一峰</name>
     4  //    <email>linapex@163.com</email>
     5  //    <wx>superexc</wx>
     6  //    <qqgroup>128148617</qqgroup>
     7  //    <url>https://jsq.ink</url>
     8  //    <role>pku engineer</role>
     9  //    <date>2019-03-16 12:09:46</date>
    10  //</624342666275983360>
    11  
    12  
    13  package core
    14  
    15  import (
    16  	"context"
    17  	"encoding/json"
    18  	"errors"
    19  	"fmt"
    20  	"io/ioutil"
    21  	"math/big"
    22  	"reflect"
    23  
    24  	"github.com/ethereum/go-ethereum/accounts"
    25  	"github.com/ethereum/go-ethereum/accounts/keystore"
    26  	"github.com/ethereum/go-ethereum/accounts/usbwallet"
    27  	"github.com/ethereum/go-ethereum/common"
    28  	"github.com/ethereum/go-ethereum/common/hexutil"
    29  	"github.com/ethereum/go-ethereum/crypto"
    30  	"github.com/ethereum/go-ethereum/internal/ethapi"
    31  	"github.com/ethereum/go-ethereum/log"
    32  	"github.com/ethereum/go-ethereum/rlp"
    33  )
    34  
    35  //ExternalAPI定义用于发出签名请求的外部API。
    36  type ExternalAPI interface {
    37  //列出可用帐户
    38  	List(ctx context.Context) (Accounts, error)
    39  //创建新帐户的新请求
    40  	New(ctx context.Context) (accounts.Account, error)
    41  //SignTransaction请求签署指定的事务
    42  	SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error)
    43  //签名-请求对给定数据进行签名(加前缀)
    44  	Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error)
    45  //ecrecover-请求执行ecrecover
    46  	EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error)
    47  //导出-请求导出帐户
    48  	Export(ctx context.Context, addr common.Address) (json.RawMessage, error)
    49  //导入-请求导入帐户
    50  	Import(ctx context.Context, keyJSON json.RawMessage) (Account, error)
    51  }
    52  
    53  //SignerRui指定UI需要实现什么方法才能用作签名者的UI
    54  type SignerUI interface {
    55  //approvetx提示用户确认请求签署交易
    56  	ApproveTx(request *SignTxRequest) (SignTxResponse, error)
    57  //ApproveSignData提示用户确认请求签署数据
    58  	ApproveSignData(request *SignDataRequest) (SignDataResponse, error)
    59  //approveexport提示用户确认导出加密帐户json
    60  	ApproveExport(request *ExportRequest) (ExportResponse, error)
    61  //approveImport提示用户确认导入账号json
    62  	ApproveImport(request *ImportRequest) (ImportResponse, error)
    63  //批准提示用户确认列出帐户
    64  //用户界面可以修改要列出的科目列表
    65  	ApproveListing(request *ListRequest) (ListResponse, error)
    66  //ApproveWaccount提示用户确认创建新帐户,并显示给调用方
    67  	ApproveNewAccount(request *NewAccountRequest) (NewAccountResponse, error)
    68  //ShowError向用户显示错误消息
    69  	ShowError(message string)
    70  //ShowInfo向用户显示信息消息
    71  	ShowInfo(message string)
    72  //OnApprovedTX通知用户界面一个事务已成功签名。
    73  //用户界面可以使用此方法跟踪发送给特定收件人的邮件数量。
    74  	OnApprovedTx(tx ethapi.SignTransactionResult)
    75  //当签名者启动时调用OnSignerStartup,并告诉用户界面有关外部API位置和版本的信息。
    76  //信息
    77  	OnSignerStartup(info StartupInfo)
    78  }
    79  
    80  //signerapi定义了externalAPI的实际实现
    81  type SignerAPI struct {
    82  	chainID   *big.Int
    83  	am        *accounts.Manager
    84  	UI        SignerUI
    85  	validator *Validator
    86  }
    87  
    88  //有关请求的元数据
    89  type Metadata struct {
    90  	Remote string `json:"remote"`
    91  	Local  string `json:"local"`
    92  	Scheme string `json:"scheme"`
    93  }
    94  
    95  //MetadataFromContext从给定的Context.Context中提取元数据
    96  func MetadataFromContext(ctx context.Context) Metadata {
    97  m := Metadata{"NA", "NA", "NA"} //蝙蝠侠
    98  
    99  	if v := ctx.Value("remote"); v != nil {
   100  		m.Remote = v.(string)
   101  	}
   102  	if v := ctx.Value("scheme"); v != nil {
   103  		m.Scheme = v.(string)
   104  	}
   105  	if v := ctx.Value("local"); v != nil {
   106  		m.Local = v.(string)
   107  	}
   108  	return m
   109  }
   110  
   111  //字符串实现字符串接口
   112  func (m Metadata) String() string {
   113  	s, err := json.Marshal(m)
   114  	if err == nil {
   115  		return string(s)
   116  	}
   117  	return err.Error()
   118  }
   119  
   120  //签名者和用户界面之间的请求/响应类型的类型
   121  type (
   122  //signtxrequest包含要签名的事务的信息
   123  	SignTxRequest struct {
   124  		Transaction SendTxArgs       `json:"transaction"`
   125  		Callinfo    []ValidationInfo `json:"call_info"`
   126  		Meta        Metadata         `json:"meta"`
   127  	}
   128  //SigntxRequest的SigntxResponse结果
   129  	SignTxResponse struct {
   130  //用户界面可以更改Tx
   131  		Transaction SendTxArgs `json:"transaction"`
   132  		Approved    bool       `json:"approved"`
   133  		Password    string     `json:"password"`
   134  	}
   135  //将有关查询的信息导出到导出帐户
   136  	ExportRequest struct {
   137  		Address common.Address `json:"address"`
   138  		Meta    Metadata       `json:"meta"`
   139  	}
   140  //导出响应对导出请求的响应
   141  	ExportResponse struct {
   142  		Approved bool `json:"approved"`
   143  	}
   144  //导入请求有关导入帐户请求的信息
   145  	ImportRequest struct {
   146  		Meta Metadata `json:"meta"`
   147  	}
   148  	ImportResponse struct {
   149  		Approved    bool   `json:"approved"`
   150  		OldPassword string `json:"old_password"`
   151  		NewPassword string `json:"new_password"`
   152  	}
   153  	SignDataRequest struct {
   154  		Address common.MixedcaseAddress `json:"address"`
   155  		Rawdata hexutil.Bytes           `json:"raw_data"`
   156  		Message string                  `json:"message"`
   157  		Hash    hexutil.Bytes           `json:"hash"`
   158  		Meta    Metadata                `json:"meta"`
   159  	}
   160  	SignDataResponse struct {
   161  		Approved bool `json:"approved"`
   162  		Password string
   163  	}
   164  	NewAccountRequest struct {
   165  		Meta Metadata `json:"meta"`
   166  	}
   167  	NewAccountResponse struct {
   168  		Approved bool   `json:"approved"`
   169  		Password string `json:"password"`
   170  	}
   171  	ListRequest struct {
   172  		Accounts []Account `json:"accounts"`
   173  		Meta     Metadata  `json:"meta"`
   174  	}
   175  	ListResponse struct {
   176  		Accounts []Account `json:"accounts"`
   177  	}
   178  	Message struct {
   179  		Text string `json:"text"`
   180  	}
   181  	StartupInfo struct {
   182  		Info map[string]interface{} `json:"info"`
   183  	}
   184  )
   185  
   186  var ErrRequestDenied = errors.New("Request denied")
   187  
   188  //NewSignerAPI创建了一个新的可用于帐户管理的API。
   189  //kslocation指定存储受密码保护的private的目录
   190  //创建新帐户时生成的键。
   191  //nousb禁用支持硬件设备所需的USB支持,如
   192  //Ledger和Trezor。
   193  func NewSignerAPI(chainID int64, ksLocation string, noUSB bool, ui SignerUI, abidb *AbiDb, lightKDF bool) *SignerAPI {
   194  	var (
   195  		backends []accounts.Backend
   196  		n, p     = keystore.StandardScryptN, keystore.StandardScryptP
   197  	)
   198  	if lightKDF {
   199  		n, p = keystore.LightScryptN, keystore.LightScryptP
   200  	}
   201  //支持基于密码的帐户
   202  	if len(ksLocation) > 0 {
   203  		backends = append(backends, keystore.NewKeyStore(ksLocation, n, p))
   204  	}
   205  	if !noUSB {
   206  //启动用于分类帐硬件钱包的USB集线器
   207  		if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
   208  			log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
   209  		} else {
   210  			backends = append(backends, ledgerhub)
   211  			log.Debug("Ledger support enabled")
   212  		}
   213  //启动Trezor硬件钱包的USB集线器
   214  		if trezorhub, err := usbwallet.NewTrezorHub(); err != nil {
   215  			log.Warn(fmt.Sprintf("Failed to start Trezor hub, disabling: %v", err))
   216  		} else {
   217  			backends = append(backends, trezorhub)
   218  			log.Debug("Trezor support enabled")
   219  		}
   220  	}
   221  	return &SignerAPI{big.NewInt(chainID), accounts.NewManager(backends...), ui, NewValidator(abidb)}
   222  }
   223  
   224  //list返回签名者管理的钱包集。每个钱包都可以包含
   225  //多个帐户。
   226  func (api *SignerAPI) List(ctx context.Context) (Accounts, error) {
   227  	var accs []Account
   228  	for _, wallet := range api.am.Wallets() {
   229  		for _, acc := range wallet.Accounts() {
   230  			acc := Account{Typ: "Account", URL: wallet.URL(), Address: acc.Address}
   231  			accs = append(accs, acc)
   232  		}
   233  	}
   234  	result, err := api.UI.ApproveListing(&ListRequest{Accounts: accs, Meta: MetadataFromContext(ctx)})
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  	if result.Accounts == nil {
   239  		return nil, ErrRequestDenied
   240  
   241  	}
   242  	return result.Accounts, nil
   243  }
   244  
   245  //新建创建新的密码保护帐户。私钥受保护
   246  //给定的密码。用户负责备份存储的私钥
   247  //在密钥库位置中,创建此API时指定了THA。
   248  func (api *SignerAPI) New(ctx context.Context) (accounts.Account, error) {
   249  	be := api.am.Backends(keystore.KeyStoreType)
   250  	if len(be) == 0 {
   251  		return accounts.Account{}, errors.New("password based accounts not supported")
   252  	}
   253  	resp, err := api.UI.ApproveNewAccount(&NewAccountRequest{MetadataFromContext(ctx)})
   254  
   255  	if err != nil {
   256  		return accounts.Account{}, err
   257  	}
   258  	if !resp.Approved {
   259  		return accounts.Account{}, ErrRequestDenied
   260  	}
   261  	return be[0].(*keystore.KeyStore).NewAccount(resp.Password)
   262  }
   263  
   264  //logdiff记录传入(原始)事务和从签名者返回的事务之间的差异。
   265  //如果修改了事务,它还返回“true”,以便可以将签名者配置为不允许
   266  //请求的用户界面修改
   267  func logDiff(original *SignTxRequest, new *SignTxResponse) bool {
   268  	modified := false
   269  	if f0, f1 := original.Transaction.From, new.Transaction.From; !reflect.DeepEqual(f0, f1) {
   270  		log.Info("Sender-account changed by UI", "was", f0, "is", f1)
   271  		modified = true
   272  	}
   273  	if t0, t1 := original.Transaction.To, new.Transaction.To; !reflect.DeepEqual(t0, t1) {
   274  		log.Info("Recipient-account changed by UI", "was", t0, "is", t1)
   275  		modified = true
   276  	}
   277  	if g0, g1 := original.Transaction.Gas, new.Transaction.Gas; g0 != g1 {
   278  		modified = true
   279  		log.Info("Gas changed by UI", "was", g0, "is", g1)
   280  	}
   281  	if g0, g1 := big.Int(original.Transaction.GasPrice), big.Int(new.Transaction.GasPrice); g0.Cmp(&g1) != 0 {
   282  		modified = true
   283  		log.Info("GasPrice changed by UI", "was", g0, "is", g1)
   284  	}
   285  	if v0, v1 := big.Int(original.Transaction.Value), big.Int(new.Transaction.Value); v0.Cmp(&v1) != 0 {
   286  		modified = true
   287  		log.Info("Value changed by UI", "was", v0, "is", v1)
   288  	}
   289  	if d0, d1 := original.Transaction.Data, new.Transaction.Data; d0 != d1 {
   290  		d0s := ""
   291  		d1s := ""
   292  		if d0 != nil {
   293  			d0s = common.ToHex(*d0)
   294  		}
   295  		if d1 != nil {
   296  			d1s = common.ToHex(*d1)
   297  		}
   298  		if d1s != d0s {
   299  			modified = true
   300  			log.Info("Data changed by UI", "was", d0s, "is", d1s)
   301  		}
   302  	}
   303  	if n0, n1 := original.Transaction.Nonce, new.Transaction.Nonce; n0 != n1 {
   304  		modified = true
   305  		log.Info("Nonce changed by UI", "was", n0, "is", n1)
   306  	}
   307  	return modified
   308  }
   309  
   310  //signTransaction对给定的事务进行签名,并将其作为json和rlp编码的形式返回
   311  func (api *SignerAPI) SignTransaction(ctx context.Context, args SendTxArgs, methodSelector *string) (*ethapi.SignTransactionResult, error) {
   312  	var (
   313  		err    error
   314  		result SignTxResponse
   315  	)
   316  	msgs, err := api.validator.ValidateTransaction(&args, methodSelector)
   317  	if err != nil {
   318  		return nil, err
   319  	}
   320  
   321  	req := SignTxRequest{
   322  		Transaction: args,
   323  		Meta:        MetadataFromContext(ctx),
   324  		Callinfo:    msgs.Messages,
   325  	}
   326  //工艺批准
   327  	result, err = api.UI.ApproveTx(&req)
   328  	if err != nil {
   329  		return nil, err
   330  	}
   331  	if !result.Approved {
   332  		return nil, ErrRequestDenied
   333  	}
   334  //记录用户界面对签名请求所做的更改
   335  	logDiff(&req, &result)
   336  	var (
   337  		acc    accounts.Account
   338  		wallet accounts.Wallet
   339  	)
   340  	acc = accounts.Account{Address: result.Transaction.From.Address()}
   341  	wallet, err = api.am.Find(acc)
   342  	if err != nil {
   343  		return nil, err
   344  	}
   345  //将字段转换为实际事务
   346  	var unsignedTx = result.Transaction.toTransaction()
   347  
   348  //要签名的是从UI返回的那个
   349  	signedTx, err := wallet.SignTxWithPassphrase(acc, result.Password, unsignedTx, api.chainID)
   350  	if err != nil {
   351  		api.UI.ShowError(err.Error())
   352  		return nil, err
   353  	}
   354  
   355  	rlpdata, err := rlp.EncodeToBytes(signedTx)
   356  	response := ethapi.SignTransactionResult{Raw: rlpdata, Tx: signedTx}
   357  
   358  //最后,将签名的Tx发送到UI
   359  	api.UI.OnApprovedTx(response)
   360  //…和外部呼叫者
   361  	return &response, nil
   362  
   363  }
   364  
   365  //sign计算以太坊ECDSA签名:
   366  //keccack256(“\x19ethereum签名消息:\n”+len(消息)+消息)
   367  //
   368  //注:生成的签名符合secp256k1曲线r、s和v值,
   369  //由于遗产原因,V值将为27或28。
   370  //
   371  //用于计算签名的密钥用给定的密码解密。
   372  //
   373  //https://github.com/ethereum/go-ethereum/wiki/management-apis个人签名
   374  func (api *SignerAPI) Sign(ctx context.Context, addr common.MixedcaseAddress, data hexutil.Bytes) (hexutil.Bytes, error) {
   375  	sighash, msg := SignHash(data)
   376  //我们在查询是否有账户之前提出请求,以防止
   377  //通过API进行帐户枚举
   378  	req := &SignDataRequest{Address: addr, Rawdata: data, Message: msg, Hash: sighash, Meta: MetadataFromContext(ctx)}
   379  	res, err := api.UI.ApproveSignData(req)
   380  
   381  	if err != nil {
   382  		return nil, err
   383  	}
   384  	if !res.Approved {
   385  		return nil, ErrRequestDenied
   386  	}
   387  //查找包含请求签名者的钱包
   388  	account := accounts.Account{Address: addr.Address()}
   389  	wallet, err := api.am.Find(account)
   390  	if err != nil {
   391  		return nil, err
   392  	}
   393  //集合用钱包签名数据
   394  	signature, err := wallet.SignHashWithPassphrase(account, res.Password, sighash)
   395  	if err != nil {
   396  		api.UI.ShowError(err.Error())
   397  		return nil, err
   398  	}
   399  signature[64] += 27 //根据黄纸将V从0/1转换为27/28
   400  	return signature, nil
   401  }
   402  
   403  //ecrecover返回用于创建签名的帐户的地址。
   404  //注意,此功能与ETH标志和个人标志兼容。因此,它恢复了
   405  //地址:
   406  //hash=keccak256(“\x19ethereum签名消息:\n”$消息长度$消息)
   407  //addr=ecrecover(哈希,签名)
   408  //
   409  //注意,签名必须符合secp256k1曲线r、s和v值,其中
   410  //由于遗留原因,V值必须是27或28。
   411  //
   412  //https://github.com/ethereum/go-ethereum/wiki/management-apis个人\u-ecrecover
   413  func (api *SignerAPI) EcRecover(ctx context.Context, data, sig hexutil.Bytes) (common.Address, error) {
   414  	if len(sig) != 65 {
   415  		return common.Address{}, fmt.Errorf("signature must be 65 bytes long")
   416  	}
   417  	if sig[64] != 27 && sig[64] != 28 {
   418  		return common.Address{}, fmt.Errorf("invalid Ethereum signature (V is not 27 or 28)")
   419  	}
   420  sig[64] -= 27 //将黄纸V从27/28转换为0/1
   421  	hash, _ := SignHash(data)
   422  	rpk, err := crypto.SigToPub(hash, sig)
   423  	if err != nil {
   424  		return common.Address{}, err
   425  	}
   426  	return crypto.PubkeyToAddress(*rpk), nil
   427  }
   428  
   429  //signhash是一个帮助函数,用于计算给定消息的哈希
   430  //安全地用于计算签名。
   431  //
   432  //哈希计算为
   433  //keccak256(“\x19ethereum签名消息:\n”$消息长度$消息)。
   434  //
   435  //这将为已签名的消息提供上下文,并防止对事务进行签名。
   436  func SignHash(data []byte) ([]byte, string) {
   437  	msg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(data), data)
   438  	return crypto.Keccak256([]byte(msg)), msg
   439  }
   440  
   441  //export以Web3密钥库格式返回与给定地址关联的加密私钥。
   442  func (api *SignerAPI) Export(ctx context.Context, addr common.Address) (json.RawMessage, error) {
   443  	res, err := api.UI.ApproveExport(&ExportRequest{Address: addr, Meta: MetadataFromContext(ctx)})
   444  
   445  	if err != nil {
   446  		return nil, err
   447  	}
   448  	if !res.Approved {
   449  		return nil, ErrRequestDenied
   450  	}
   451  //查找包含请求签名者的钱包
   452  	wallet, err := api.am.Find(accounts.Account{Address: addr})
   453  	if err != nil {
   454  		return nil, err
   455  	}
   456  	if wallet.URL().Scheme != keystore.KeyStoreScheme {
   457  		return nil, fmt.Errorf("Account is not a keystore-account")
   458  	}
   459  	return ioutil.ReadFile(wallet.URL().Path)
   460  }
   461  
   462  //import尝试在本地密钥库中导入给定的keyjson。keyjson数据应为
   463  //以Web3密钥库格式。它将使用给定的密码短语解密keyjson,并在成功时
   464  //解密它将使用给定的新密码短语加密密钥,并将其存储在密钥库中。
   465  func (api *SignerAPI) Import(ctx context.Context, keyJSON json.RawMessage) (Account, error) {
   466  	be := api.am.Backends(keystore.KeyStoreType)
   467  
   468  	if len(be) == 0 {
   469  		return Account{}, errors.New("password based accounts not supported")
   470  	}
   471  	res, err := api.UI.ApproveImport(&ImportRequest{Meta: MetadataFromContext(ctx)})
   472  
   473  	if err != nil {
   474  		return Account{}, err
   475  	}
   476  	if !res.Approved {
   477  		return Account{}, ErrRequestDenied
   478  	}
   479  	acc, err := be[0].(*keystore.KeyStore).Import(keyJSON, res.OldPassword, res.NewPassword)
   480  	if err != nil {
   481  		api.UI.ShowError(err.Error())
   482  		return Account{}, err
   483  	}
   484  	return Account{Typ: "Account", URL: acc.URL, Address: acc.Address}, nil
   485  }
   486