github.com/linapex/ethereum-go-chinese@v0.0.0-20190316121929-f8b7a73c3fa1/signer/core/abihelper.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 19:16:42</date>
    10  //</624450110155132928>
    11  
    12  
    13  package core
    14  
    15  import (
    16  	"encoding/json"
    17  	"fmt"
    18  	"io/ioutil"
    19  	"strings"
    20  
    21  	"github.com/ethereum/go-ethereum/accounts/abi"
    22  	"github.com/ethereum/go-ethereum/common"
    23  
    24  	"bytes"
    25  	"os"
    26  	"regexp"
    27  )
    28  
    29  type decodedArgument struct {
    30  	soltype abi.Argument
    31  	value   interface{}
    32  }
    33  type decodedCallData struct {
    34  	signature string
    35  	name      string
    36  	inputs    []decodedArgument
    37  }
    38  
    39  //字符串实现Stringer接口,尝试使用基础值类型
    40  func (arg decodedArgument) String() string {
    41  	var value string
    42  	switch val := arg.value.(type) {
    43  	case fmt.Stringer:
    44  		value = val.String()
    45  	default:
    46  		value = fmt.Sprintf("%v", val)
    47  	}
    48  	return fmt.Sprintf("%v: %v", arg.soltype.Type.String(), value)
    49  }
    50  
    51  //字符串实现用于decodedcalldata的字符串接口
    52  func (cd decodedCallData) String() string {
    53  	args := make([]string, len(cd.inputs))
    54  	for i, arg := range cd.inputs {
    55  		args[i] = arg.String()
    56  	}
    57  	return fmt.Sprintf("%s(%s)", cd.name, strings.Join(args, ","))
    58  }
    59  
    60  //ParseCallData将提供的调用数据与ABI定义匹配,
    61  //并返回包含实际go类型值的结构
    62  func parseCallData(calldata []byte, abidata string) (*decodedCallData, error) {
    63  
    64  	if len(calldata) < 4 {
    65  		return nil, fmt.Errorf("Invalid ABI-data, incomplete method signature of (%d bytes)", len(calldata))
    66  	}
    67  
    68  	sigdata, argdata := calldata[:4], calldata[4:]
    69  	if len(argdata)%32 != 0 {
    70  		return nil, fmt.Errorf("Not ABI-encoded data; length should be a multiple of 32 (was %d)", len(argdata))
    71  	}
    72  
    73  	abispec, err := abi.JSON(strings.NewReader(abidata))
    74  	if err != nil {
    75  		return nil, fmt.Errorf("Failed parsing JSON ABI: %v, abidata: %v", err, abidata)
    76  	}
    77  
    78  	method, err := abispec.MethodById(sigdata)
    79  	if err != nil {
    80  		return nil, err
    81  	}
    82  
    83  	v, err := method.Inputs.UnpackValues(argdata)
    84  	if err != nil {
    85  		return nil, err
    86  	}
    87  
    88  	decoded := decodedCallData{signature: method.Sig(), name: method.Name}
    89  
    90  	for n, argument := range method.Inputs {
    91  		if err != nil {
    92  			return nil, fmt.Errorf("Failed to decode argument %d (signature %v): %v", n, method.Sig(), err)
    93  		}
    94  		decodedArg := decodedArgument{
    95  			soltype: argument,
    96  			value:   v[n],
    97  		}
    98  		decoded.inputs = append(decoded.inputs, decodedArg)
    99  	}
   100  
   101  //数据解码完毕。此时,我们对解码后的数据进行编码,以查看它是否与
   102  //原始数据。如果我们不这样做,就可以在参数中填充额外的数据,例如
   103  //仅通过解码数据检测不到。
   104  
   105  	var (
   106  		encoded []byte
   107  	)
   108  	encoded, err = method.Inputs.PackValues(v)
   109  
   110  	if err != nil {
   111  		return nil, err
   112  	}
   113  
   114  	if !bytes.Equal(encoded, argdata) {
   115  		was := common.Bytes2Hex(encoded)
   116  		exp := common.Bytes2Hex(argdata)
   117  		return nil, fmt.Errorf("WARNING: Supplied data is stuffed with extra data. \nWant %s\nHave %s\nfor method %v", exp, was, method.Sig())
   118  	}
   119  	return &decoded, nil
   120  }
   121  
   122  //methodSelectorToAbi将方法选择器转换为ABI结构。返回的数据是有效的JSON字符串
   123  //可由标准ABI包装使用。
   124  func MethodSelectorToAbi(selector string) ([]byte, error) {
   125  
   126  	re := regexp.MustCompile(`^([^\)]+)\(([a-z0-9,\[\]]*)\)`)
   127  
   128  	type fakeArg struct {
   129  		Type string `json:"type"`
   130  	}
   131  	type fakeABI struct {
   132  		Name   string    `json:"name"`
   133  		Type   string    `json:"type"`
   134  		Inputs []fakeArg `json:"inputs"`
   135  	}
   136  	groups := re.FindStringSubmatch(selector)
   137  	if len(groups) != 3 {
   138  		return nil, fmt.Errorf("Did not match: %v (%v matches)", selector, len(groups))
   139  	}
   140  	name := groups[1]
   141  	args := groups[2]
   142  	arguments := make([]fakeArg, 0)
   143  	if len(args) > 0 {
   144  		for _, arg := range strings.Split(args, ",") {
   145  			arguments = append(arguments, fakeArg{arg})
   146  		}
   147  	}
   148  	abicheat := fakeABI{
   149  		name, "function", arguments,
   150  	}
   151  	return json.Marshal([]fakeABI{abicheat})
   152  
   153  }
   154  
   155  type AbiDb struct {
   156  	db           map[string]string
   157  	customdb     map[string]string
   158  	customdbPath string
   159  }
   160  
   161  //出于测试目的,存在newEmptyBidb
   162  func NewEmptyAbiDB() (*AbiDb, error) {
   163  	return &AbiDb{make(map[string]string), make(map[string]string), ""}, nil
   164  }
   165  
   166  //newabidbfrmfile从文件加载签名数据库,以及
   167  //如果文件不是有效的JSON,则会出错。没有其他内容验证
   168  func NewAbiDBFromFile(path string) (*AbiDb, error) {
   169  	raw, err := ioutil.ReadFile(path)
   170  	if err != nil {
   171  		return nil, err
   172  	}
   173  	db, err := NewEmptyAbiDB()
   174  	if err != nil {
   175  		return nil, err
   176  	}
   177  	json.Unmarshal(raw, &db.db)
   178  	return db, nil
   179  }
   180  
   181  //newabidbfrmfiles同时加载标准签名数据库和自定义数据库。后者将被使用
   182  //如果通过API提交新值,则将其写入
   183  func NewAbiDBFromFiles(standard, custom string) (*AbiDb, error) {
   184  
   185  	db := &AbiDb{make(map[string]string), make(map[string]string), custom}
   186  	db.customdbPath = custom
   187  
   188  	raw, err := ioutil.ReadFile(standard)
   189  	if err != nil {
   190  		return nil, err
   191  	}
   192  	json.Unmarshal(raw, &db.db)
   193  //自定义文件可能不存在。如果需要,将在保存期间创建
   194  	if _, err := os.Stat(custom); err == nil {
   195  		raw, err = ioutil.ReadFile(custom)
   196  		if err != nil {
   197  			return nil, err
   198  		}
   199  		json.Unmarshal(raw, &db.customdb)
   200  	}
   201  
   202  	return db, nil
   203  }
   204  
   205  //LookupMethodSelector对照已知的ABI方法检查给定的4字节序列。
   206  //obs:此方法不验证匹配,假定调用方将验证匹配
   207  func (db *AbiDb) LookupMethodSelector(id []byte) (string, error) {
   208  	if len(id) < 4 {
   209  		return "", fmt.Errorf("Expected 4-byte id, got %d", len(id))
   210  	}
   211  	sig := common.ToHex(id[:4])
   212  	if key, exists := db.db[sig]; exists {
   213  		return key, nil
   214  	}
   215  	if key, exists := db.customdb[sig]; exists {
   216  		return key, nil
   217  	}
   218  	return "", fmt.Errorf("Signature %v not found", sig)
   219  }
   220  func (db *AbiDb) Size() int {
   221  	return len(db.db)
   222  }
   223  
   224  //savecustomabi临时保存签名。如果使用自定义文件,也将保存到磁盘
   225  func (db *AbiDb) saveCustomAbi(selector, signature string) error {
   226  	db.customdb[signature] = selector
   227  	if db.customdbPath == "" {
   228  return nil //本身不是错误,只是没有使用
   229  	}
   230  	d, err := json.Marshal(db.customdb)
   231  	if err != nil {
   232  		return err
   233  	}
   234  	err = ioutil.WriteFile(db.customdbPath, d, 0600)
   235  	return err
   236  }
   237  
   238  //如果启用了自定义数据库保存,则向数据库添加签名。
   239  //OBS:这种方法不能验证数据的正确性,
   240  //假定呼叫者已经这样做了
   241  func (db *AbiDb) AddSignature(selector string, data []byte) error {
   242  	if len(data) < 4 {
   243  		return nil
   244  	}
   245  	_, err := db.LookupMethodSelector(data[:4])
   246  	if err == nil {
   247  		return nil
   248  	}
   249  	sig := common.ToHex(data[:4])
   250  	return db.saveCustomAbi(selector, sig)
   251  }
   252