461 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			461 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|   | package api | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"encoding/binary" | ||
|  | 	"encoding/hex" | ||
|  | 	"encoding/json" | ||
|  | 	"math/big" | ||
|  | 	"strings" | ||
|  | 
 | ||
|  | 	"github.com/ethereum/go-ethereum/common" | ||
|  | 	"github.com/ethereum/go-ethereum/core/types" | ||
|  | 	"github.com/ethereum/go-ethereum/rpc/shared" | ||
|  | ) | ||
|  | 
 | ||
|  | type hexdata struct { | ||
|  | 	data  []byte | ||
|  | 	isNil bool | ||
|  | } | ||
|  | 
 | ||
|  | func (d *hexdata) String() string { | ||
|  | 	return "0x" + common.Bytes2Hex(d.data) | ||
|  | } | ||
|  | 
 | ||
|  | func (d *hexdata) MarshalJSON() ([]byte, error) { | ||
|  | 	if d.isNil { | ||
|  | 		return json.Marshal(nil) | ||
|  | 	} | ||
|  | 	return json.Marshal(d.String()) | ||
|  | } | ||
|  | 
 | ||
|  | func newHexData(input interface{}) *hexdata { | ||
|  | 	d := new(hexdata) | ||
|  | 
 | ||
|  | 	if input == nil { | ||
|  | 		d.isNil = true | ||
|  | 		return d | ||
|  | 	} | ||
|  | 	switch input := input.(type) { | ||
|  | 	case []byte: | ||
|  | 		d.data = input | ||
|  | 	case common.Hash: | ||
|  | 		d.data = input.Bytes() | ||
|  | 	case *common.Hash: | ||
|  | 		if input == nil { | ||
|  | 			d.isNil = true | ||
|  | 		} else { | ||
|  | 			d.data = input.Bytes() | ||
|  | 		} | ||
|  | 	case common.Address: | ||
|  | 		d.data = input.Bytes() | ||
|  | 	case *common.Address: | ||
|  | 		if input == nil { | ||
|  | 			d.isNil = true | ||
|  | 		} else { | ||
|  | 			d.data = input.Bytes() | ||
|  | 		} | ||
|  | 	case types.Bloom: | ||
|  | 		d.data = input.Bytes() | ||
|  | 	case *types.Bloom: | ||
|  | 		if input == nil { | ||
|  | 			d.isNil = true | ||
|  | 		} else { | ||
|  | 			d.data = input.Bytes() | ||
|  | 		} | ||
|  | 	case *big.Int: | ||
|  | 		if input == nil { | ||
|  | 			d.isNil = true | ||
|  | 		} else { | ||
|  | 			d.data = input.Bytes() | ||
|  | 		} | ||
|  | 	case int64: | ||
|  | 		d.data = big.NewInt(input).Bytes() | ||
|  | 	case uint64: | ||
|  | 		buff := make([]byte, 8) | ||
|  | 		binary.BigEndian.PutUint64(buff, input) | ||
|  | 		d.data = buff | ||
|  | 	case int: | ||
|  | 		d.data = big.NewInt(int64(input)).Bytes() | ||
|  | 	case uint: | ||
|  | 		d.data = big.NewInt(int64(input)).Bytes() | ||
|  | 	case int8: | ||
|  | 		d.data = big.NewInt(int64(input)).Bytes() | ||
|  | 	case uint8: | ||
|  | 		d.data = big.NewInt(int64(input)).Bytes() | ||
|  | 	case int16: | ||
|  | 		d.data = big.NewInt(int64(input)).Bytes() | ||
|  | 	case uint16: | ||
|  | 		buff := make([]byte, 2) | ||
|  | 		binary.BigEndian.PutUint16(buff, input) | ||
|  | 		d.data = buff | ||
|  | 	case int32: | ||
|  | 		d.data = big.NewInt(int64(input)).Bytes() | ||
|  | 	case uint32: | ||
|  | 		buff := make([]byte, 4) | ||
|  | 		binary.BigEndian.PutUint32(buff, input) | ||
|  | 		d.data = buff | ||
|  | 	case string: // hexstring | ||
|  | 		// aaargh ffs TODO: avoid back-and-forth hex encodings where unneeded | ||
|  | 		bytes, err := hex.DecodeString(strings.TrimPrefix(input, "0x")) | ||
|  | 		if err != nil { | ||
|  | 			d.isNil = true | ||
|  | 		} else { | ||
|  | 			d.data = bytes | ||
|  | 		} | ||
|  | 	default: | ||
|  | 		d.isNil = true | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return d | ||
|  | } | ||
|  | 
 | ||
|  | type hexnum struct { | ||
|  | 	data  []byte | ||
|  | 	isNil bool | ||
|  | } | ||
|  | 
 | ||
|  | func (d *hexnum) String() string { | ||
|  | 	// Get hex string from bytes | ||
|  | 	out := common.Bytes2Hex(d.data) | ||
|  | 	// Trim leading 0s | ||
|  | 	out = strings.TrimLeft(out, "0") | ||
|  | 	// Output "0x0" when value is 0 | ||
|  | 	if len(out) == 0 { | ||
|  | 		out = "0" | ||
|  | 	} | ||
|  | 	return "0x" + out | ||
|  | } | ||
|  | 
 | ||
|  | func (d *hexnum) MarshalJSON() ([]byte, error) { | ||
|  | 	if d.isNil { | ||
|  | 		return json.Marshal(nil) | ||
|  | 	} | ||
|  | 	return json.Marshal(d.String()) | ||
|  | } | ||
|  | 
 | ||
|  | func newHexNum(input interface{}) *hexnum { | ||
|  | 	d := new(hexnum) | ||
|  | 
 | ||
|  | 	d.data = newHexData(input).data | ||
|  | 
 | ||
|  | 	return d | ||
|  | } | ||
|  | 
 | ||
|  | type BlockRes struct { | ||
|  | 	fullTx bool | ||
|  | 
 | ||
|  | 	BlockNumber     *hexnum           `json:"number"` | ||
|  | 	BlockHash       *hexdata          `json:"hash"` | ||
|  | 	ParentHash      *hexdata          `json:"parentHash"` | ||
|  | 	Nonce           *hexdata          `json:"nonce"` | ||
|  | 	Sha3Uncles      *hexdata          `json:"sha3Uncles"` | ||
|  | 	LogsBloom       *hexdata          `json:"logsBloom"` | ||
|  | 	TransactionRoot *hexdata          `json:"transactionsRoot"` | ||
|  | 	StateRoot       *hexdata          `json:"stateRoot"` | ||
|  | 	Miner           *hexdata          `json:"miner"` | ||
|  | 	Difficulty      *hexnum           `json:"difficulty"` | ||
|  | 	TotalDifficulty *hexnum           `json:"totalDifficulty"` | ||
|  | 	Size            *hexnum           `json:"size"` | ||
|  | 	ExtraData       *hexdata          `json:"extraData"` | ||
|  | 	GasLimit        *hexnum           `json:"gasLimit"` | ||
|  | 	GasUsed         *hexnum           `json:"gasUsed"` | ||
|  | 	UnixTimestamp   *hexnum           `json:"timestamp"` | ||
|  | 	Transactions    []*TransactionRes `json:"transactions"` | ||
|  | 	Uncles          []*UncleRes       `json:"uncles"` | ||
|  | } | ||
|  | 
 | ||
|  | func (b *BlockRes) MarshalJSON() ([]byte, error) { | ||
|  | 	if b.fullTx { | ||
|  | 		var ext struct { | ||
|  | 			BlockNumber     *hexnum           `json:"number"` | ||
|  | 			BlockHash       *hexdata          `json:"hash"` | ||
|  | 			ParentHash      *hexdata          `json:"parentHash"` | ||
|  | 			Nonce           *hexdata          `json:"nonce"` | ||
|  | 			Sha3Uncles      *hexdata          `json:"sha3Uncles"` | ||
|  | 			LogsBloom       *hexdata          `json:"logsBloom"` | ||
|  | 			TransactionRoot *hexdata          `json:"transactionsRoot"` | ||
|  | 			StateRoot       *hexdata          `json:"stateRoot"` | ||
|  | 			Miner           *hexdata          `json:"miner"` | ||
|  | 			Difficulty      *hexnum           `json:"difficulty"` | ||
|  | 			TotalDifficulty *hexnum           `json:"totalDifficulty"` | ||
|  | 			Size            *hexnum           `json:"size"` | ||
|  | 			ExtraData       *hexdata          `json:"extraData"` | ||
|  | 			GasLimit        *hexnum           `json:"gasLimit"` | ||
|  | 			GasUsed         *hexnum           `json:"gasUsed"` | ||
|  | 			UnixTimestamp   *hexnum           `json:"timestamp"` | ||
|  | 			Transactions    []*TransactionRes `json:"transactions"` | ||
|  | 			Uncles          []*hexdata        `json:"uncles"` | ||
|  | 		} | ||
|  | 
 | ||
|  | 		ext.BlockNumber = b.BlockNumber | ||
|  | 		ext.BlockHash = b.BlockHash | ||
|  | 		ext.ParentHash = b.ParentHash | ||
|  | 		ext.Nonce = b.Nonce | ||
|  | 		ext.Sha3Uncles = b.Sha3Uncles | ||
|  | 		ext.LogsBloom = b.LogsBloom | ||
|  | 		ext.TransactionRoot = b.TransactionRoot | ||
|  | 		ext.StateRoot = b.StateRoot | ||
|  | 		ext.Miner = b.Miner | ||
|  | 		ext.Difficulty = b.Difficulty | ||
|  | 		ext.TotalDifficulty = b.TotalDifficulty | ||
|  | 		ext.Size = b.Size | ||
|  | 		ext.ExtraData = b.ExtraData | ||
|  | 		ext.GasLimit = b.GasLimit | ||
|  | 		ext.GasUsed = b.GasUsed | ||
|  | 		ext.UnixTimestamp = b.UnixTimestamp | ||
|  | 		ext.Transactions = b.Transactions | ||
|  | 		ext.Uncles = make([]*hexdata, len(b.Uncles)) | ||
|  | 		for i, u := range b.Uncles { | ||
|  | 			ext.Uncles[i] = u.BlockHash | ||
|  | 		} | ||
|  | 		return json.Marshal(ext) | ||
|  | 	} else { | ||
|  | 		var ext struct { | ||
|  | 			BlockNumber     *hexnum    `json:"number"` | ||
|  | 			BlockHash       *hexdata   `json:"hash"` | ||
|  | 			ParentHash      *hexdata   `json:"parentHash"` | ||
|  | 			Nonce           *hexdata   `json:"nonce"` | ||
|  | 			Sha3Uncles      *hexdata   `json:"sha3Uncles"` | ||
|  | 			LogsBloom       *hexdata   `json:"logsBloom"` | ||
|  | 			TransactionRoot *hexdata   `json:"transactionsRoot"` | ||
|  | 			StateRoot       *hexdata   `json:"stateRoot"` | ||
|  | 			Miner           *hexdata   `json:"miner"` | ||
|  | 			Difficulty      *hexnum    `json:"difficulty"` | ||
|  | 			TotalDifficulty *hexnum    `json:"totalDifficulty"` | ||
|  | 			Size            *hexnum    `json:"size"` | ||
|  | 			ExtraData       *hexdata   `json:"extraData"` | ||
|  | 			GasLimit        *hexnum    `json:"gasLimit"` | ||
|  | 			GasUsed         *hexnum    `json:"gasUsed"` | ||
|  | 			UnixTimestamp   *hexnum    `json:"timestamp"` | ||
|  | 			Transactions    []*hexdata `json:"transactions"` | ||
|  | 			Uncles          []*hexdata `json:"uncles"` | ||
|  | 		} | ||
|  | 
 | ||
|  | 		ext.BlockNumber = b.BlockNumber | ||
|  | 		ext.BlockHash = b.BlockHash | ||
|  | 		ext.ParentHash = b.ParentHash | ||
|  | 		ext.Nonce = b.Nonce | ||
|  | 		ext.Sha3Uncles = b.Sha3Uncles | ||
|  | 		ext.LogsBloom = b.LogsBloom | ||
|  | 		ext.TransactionRoot = b.TransactionRoot | ||
|  | 		ext.StateRoot = b.StateRoot | ||
|  | 		ext.Miner = b.Miner | ||
|  | 		ext.Difficulty = b.Difficulty | ||
|  | 		ext.TotalDifficulty = b.TotalDifficulty | ||
|  | 		ext.Size = b.Size | ||
|  | 		ext.ExtraData = b.ExtraData | ||
|  | 		ext.GasLimit = b.GasLimit | ||
|  | 		ext.GasUsed = b.GasUsed | ||
|  | 		ext.UnixTimestamp = b.UnixTimestamp | ||
|  | 		ext.Transactions = make([]*hexdata, len(b.Transactions)) | ||
|  | 		for i, tx := range b.Transactions { | ||
|  | 			ext.Transactions[i] = tx.Hash | ||
|  | 		} | ||
|  | 		ext.Uncles = make([]*hexdata, len(b.Uncles)) | ||
|  | 		for i, u := range b.Uncles { | ||
|  | 			ext.Uncles[i] = u.BlockHash | ||
|  | 		} | ||
|  | 		return json.Marshal(ext) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func NewBlockRes(block *types.Block, fullTx bool) *BlockRes { | ||
|  | 	if block == nil { | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	res := new(BlockRes) | ||
|  | 	res.fullTx = fullTx | ||
|  | 	res.BlockNumber = newHexNum(block.Number()) | ||
|  | 	res.BlockHash = newHexData(block.Hash()) | ||
|  | 	res.ParentHash = newHexData(block.ParentHash()) | ||
|  | 	res.Nonce = newHexData(block.Nonce()) | ||
|  | 	res.Sha3Uncles = newHexData(block.Header().UncleHash) | ||
|  | 	res.LogsBloom = newHexData(block.Bloom()) | ||
|  | 	res.TransactionRoot = newHexData(block.Header().TxHash) | ||
|  | 	res.StateRoot = newHexData(block.Root()) | ||
|  | 	res.Miner = newHexData(block.Header().Coinbase) | ||
|  | 	res.Difficulty = newHexNum(block.Difficulty()) | ||
|  | 	res.TotalDifficulty = newHexNum(block.Td) | ||
|  | 	res.Size = newHexNum(block.Size().Int64()) | ||
|  | 	res.ExtraData = newHexData(block.Header().Extra) | ||
|  | 	res.GasLimit = newHexNum(block.GasLimit()) | ||
|  | 	res.GasUsed = newHexNum(block.GasUsed()) | ||
|  | 	res.UnixTimestamp = newHexNum(block.Time()) | ||
|  | 
 | ||
|  | 	res.Transactions = make([]*TransactionRes, len(block.Transactions())) | ||
|  | 	for i, tx := range block.Transactions() { | ||
|  | 		res.Transactions[i] = NewTransactionRes(tx) | ||
|  | 		res.Transactions[i].BlockHash = res.BlockHash | ||
|  | 		res.Transactions[i].BlockNumber = res.BlockNumber | ||
|  | 		res.Transactions[i].TxIndex = newHexNum(i) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	res.Uncles = make([]*UncleRes, len(block.Uncles())) | ||
|  | 	for i, uncle := range block.Uncles() { | ||
|  | 		res.Uncles[i] = NewUncleRes(uncle) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return res | ||
|  | } | ||
|  | 
 | ||
|  | type TransactionRes struct { | ||
|  | 	Hash        *hexdata `json:"hash"` | ||
|  | 	Nonce       *hexnum  `json:"nonce"` | ||
|  | 	BlockHash   *hexdata `json:"blockHash"` | ||
|  | 	BlockNumber *hexnum  `json:"blockNumber"` | ||
|  | 	TxIndex     *hexnum  `json:"transactionIndex"` | ||
|  | 	From        *hexdata `json:"from"` | ||
|  | 	To          *hexdata `json:"to"` | ||
|  | 	Value       *hexnum  `json:"value"` | ||
|  | 	Gas         *hexnum  `json:"gas"` | ||
|  | 	GasPrice    *hexnum  `json:"gasPrice"` | ||
|  | 	Input       *hexdata `json:"input"` | ||
|  | } | ||
|  | 
 | ||
|  | func NewTransactionRes(tx *types.Transaction) *TransactionRes { | ||
|  | 	if tx == nil { | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var v = new(TransactionRes) | ||
|  | 	v.Hash = newHexData(tx.Hash()) | ||
|  | 	v.Nonce = newHexNum(tx.Nonce()) | ||
|  | 	// v.BlockHash = | ||
|  | 	// v.BlockNumber = | ||
|  | 	// v.TxIndex = | ||
|  | 	from, _ := tx.From() | ||
|  | 	v.From = newHexData(from) | ||
|  | 	v.To = newHexData(tx.To()) | ||
|  | 	v.Value = newHexNum(tx.Value()) | ||
|  | 	v.Gas = newHexNum(tx.Gas()) | ||
|  | 	v.GasPrice = newHexNum(tx.GasPrice()) | ||
|  | 	v.Input = newHexData(tx.Data()) | ||
|  | 	return v | ||
|  | } | ||
|  | 
 | ||
|  | type UncleRes struct { | ||
|  | 	BlockNumber     *hexnum  `json:"number"` | ||
|  | 	BlockHash       *hexdata `json:"hash"` | ||
|  | 	ParentHash      *hexdata `json:"parentHash"` | ||
|  | 	Nonce           *hexdata `json:"nonce"` | ||
|  | 	Sha3Uncles      *hexdata `json:"sha3Uncles"` | ||
|  | 	ReceiptHash     *hexdata `json:"receiptHash"` | ||
|  | 	LogsBloom       *hexdata `json:"logsBloom"` | ||
|  | 	TransactionRoot *hexdata `json:"transactionsRoot"` | ||
|  | 	StateRoot       *hexdata `json:"stateRoot"` | ||
|  | 	Miner           *hexdata `json:"miner"` | ||
|  | 	Difficulty      *hexnum  `json:"difficulty"` | ||
|  | 	ExtraData       *hexdata `json:"extraData"` | ||
|  | 	GasLimit        *hexnum  `json:"gasLimit"` | ||
|  | 	GasUsed         *hexnum  `json:"gasUsed"` | ||
|  | 	UnixTimestamp   *hexnum  `json:"timestamp"` | ||
|  | } | ||
|  | 
 | ||
|  | func NewUncleRes(h *types.Header) *UncleRes { | ||
|  | 	if h == nil { | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var v = new(UncleRes) | ||
|  | 	v.BlockNumber = newHexNum(h.Number) | ||
|  | 	v.BlockHash = newHexData(h.Hash()) | ||
|  | 	v.ParentHash = newHexData(h.ParentHash) | ||
|  | 	v.Sha3Uncles = newHexData(h.UncleHash) | ||
|  | 	v.Nonce = newHexData(h.Nonce[:]) | ||
|  | 	v.LogsBloom = newHexData(h.Bloom) | ||
|  | 	v.TransactionRoot = newHexData(h.TxHash) | ||
|  | 	v.StateRoot = newHexData(h.Root) | ||
|  | 	v.Miner = newHexData(h.Coinbase) | ||
|  | 	v.Difficulty = newHexNum(h.Difficulty) | ||
|  | 	v.ExtraData = newHexData(h.Extra) | ||
|  | 	v.GasLimit = newHexNum(h.GasLimit) | ||
|  | 	v.GasUsed = newHexNum(h.GasUsed) | ||
|  | 	v.UnixTimestamp = newHexNum(h.Time) | ||
|  | 	v.ReceiptHash = newHexData(h.ReceiptHash) | ||
|  | 
 | ||
|  | 	return v | ||
|  | } | ||
|  | 
 | ||
|  | // type FilterLogRes struct { | ||
|  | // 	Hash             string `json:"hash"` | ||
|  | // 	Address          string `json:"address"` | ||
|  | // 	Data             string `json:"data"` | ||
|  | // 	BlockNumber      string `json:"blockNumber"` | ||
|  | // 	TransactionHash  string `json:"transactionHash"` | ||
|  | // 	BlockHash        string `json:"blockHash"` | ||
|  | // 	TransactionIndex string `json:"transactionIndex"` | ||
|  | // 	LogIndex         string `json:"logIndex"` | ||
|  | // } | ||
|  | 
 | ||
|  | // type FilterWhisperRes struct { | ||
|  | // 	Hash       string `json:"hash"` | ||
|  | // 	From       string `json:"from"` | ||
|  | // 	To         string `json:"to"` | ||
|  | // 	Expiry     string `json:"expiry"` | ||
|  | // 	Sent       string `json:"sent"` | ||
|  | // 	Ttl        string `json:"ttl"` | ||
|  | // 	Topics     string `json:"topics"` | ||
|  | // 	Payload    string `json:"payload"` | ||
|  | // 	WorkProved string `json:"workProved"` | ||
|  | // } | ||
|  | 
 | ||
|  | func numString(raw interface{}) (*big.Int, error) { | ||
|  | 	var number *big.Int | ||
|  | 	// Parse as integer | ||
|  | 	num, ok := raw.(float64) | ||
|  | 	if ok { | ||
|  | 		number = big.NewInt(int64(num)) | ||
|  | 		return number, nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Parse as string/hexstring | ||
|  | 	str, ok := raw.(string) | ||
|  | 	if ok { | ||
|  | 		number = common.String2Big(str) | ||
|  | 		return number, nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return nil, shared.NewInvalidTypeError("", "not a number or string") | ||
|  | } | ||
|  | 
 | ||
|  | func blockHeight(raw interface{}, number *int64) error { | ||
|  | 	// Parse as integer | ||
|  | 	num, ok := raw.(float64) | ||
|  | 	if ok { | ||
|  | 		*number = int64(num) | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Parse as string/hexstring | ||
|  | 	str, ok := raw.(string) | ||
|  | 	if !ok { | ||
|  | 		return shared.NewInvalidTypeError("", "not a number or string") | ||
|  | 	} | ||
|  | 
 | ||
|  | 	switch str { | ||
|  | 	case "earliest": | ||
|  | 		*number = 0 | ||
|  | 	case "latest": | ||
|  | 		*number = -1 | ||
|  | 	case "pending": | ||
|  | 		*number = -2 | ||
|  | 	default: | ||
|  | 		if common.HasHexPrefix(str) { | ||
|  | 			*number = common.String2Big(str).Int64() | ||
|  | 		} else { | ||
|  | 			return shared.NewInvalidTypeError("blockNumber", "is not a valid string") | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | func blockHeightFromJson(msg json.RawMessage, number *int64) error { | ||
|  | 	var raw interface{} | ||
|  | 	if err := json.Unmarshal(msg, &raw); err != nil { | ||
|  | 		return shared.NewDecodeParamError(err.Error()) | ||
|  | 	} | ||
|  | 	return blockHeight(raw, number) | ||
|  | } |