api: ethrpc: implement eth_sendRawTransaction (#9334)

Co-authored-by: Raúl Kripalani <raul@protocol.ai>
This commit is contained in:
Kevin Li 2022-09-29 16:46:59 -04:00 committed by vyzo
parent b53d5924a1
commit 6c7fd94aa7
32 changed files with 1364 additions and 127 deletions

View File

@ -771,10 +771,10 @@ type FullNode interface {
EthGetBlockTransactionCountByHash(ctx context.Context, blkHash EthHash) (EthInt, error) //perm:read
EthGetBlockByHash(ctx context.Context, blkHash EthHash, fullTxInfo bool) (EthBlock, error) //perm:read
EthGetBlockByNumber(ctx context.Context, blkNum EthInt, fullTxInfo bool) (EthBlock, error) //perm:read
EthGetTransactionByHash(ctx context.Context, txHash EthHash) (EthTx, error) //perm:read
EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (EthBlock, error) //perm:read
EthGetTransactionByHash(ctx context.Context, txHash *EthHash) (*EthTx, error) //perm:read
EthGetTransactionCount(ctx context.Context, sender EthAddress, blkOpt string) (EthInt, error) //perm:read
EthGetTransactionReceipt(ctx context.Context, txHash EthHash) (EthTxReceipt, error) //perm:read
EthGetTransactionReceipt(ctx context.Context, txHash EthHash) (*EthTxReceipt, error) //perm:read
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash EthHash, txIndex EthInt) (EthTx, error) //perm:read
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum EthInt, txIndex EthInt) (EthTx, error) //perm:read
@ -787,9 +787,9 @@ type FullNode interface {
EthProtocolVersion(ctx context.Context) (EthInt, error) //perm:read
EthGasPrice(ctx context.Context) (EthBigInt, error) //perm:read
EthMaxPriorityFeePerGas(ctx context.Context) (EthBigInt, error) //perm:read
EthEstimateGas(ctx context.Context, tx EthCall, blkParam string) (EthInt, error) //perm:read
EthCall(ctx context.Context, tx EthCall, blkParam string) (EthBytes, error) //perm:read
EthMaxPriorityFeePerGas(ctx context.Context) (EthBigInt, error) //perm:read
EthEstimateGas(ctx context.Context, tx EthCall) (EthInt, error) //perm:read
EthCall(ctx context.Context, tx EthCall, blkParam string) (EthBytes, error) //perm:read
EthSendRawTransaction(ctx context.Context, rawTx EthBytes) (EthHash, error) //perm:read

View File

@ -346,6 +346,7 @@ func init() {
Conns: 4,
FD: 5,
})
addExample(map[string]bitfield.BitField{
"": bitfield.NewFromSet([]uint64{5, 6, 7, 10}),
})
@ -361,6 +362,15 @@ func init() {
Headers: nil,
},
})
ethint := api.EthInt(5)
addExample(ethint)
addExample(&ethint)
ethaddr, _ := api.EthAddressFromHex("0x5CbEeCF99d3fDB3f25E309Cc264f240bb0664031")
addExample(&ethaddr)
ethhash, _ := api.EthHashFromCid(c)
addExample(&ethhash)
}
func GetAPIType(name, pkg string) (i interface{}, t reflect.Type, permStruct []reflect.Type) {

478
api/eth_transactions.go Normal file
View File

@ -0,0 +1,478 @@
package api
import (
"bytes"
"encoding/binary"
"fmt"
mathbig "math/big"
"golang.org/x/crypto/sha3"
xerrors "golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
gocrypto "github.com/filecoin-project/go-crypto"
"github.com/filecoin-project/go-state-types/big"
builtintypes "github.com/filecoin-project/go-state-types/builtin"
"github.com/filecoin-project/go-state-types/builtin/v8/evm"
init8 "github.com/filecoin-project/go-state-types/builtin/v8/init"
typescrypto "github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
"github.com/filecoin-project/lotus/chain/types"
)
type EthTx struct {
ChainID EthInt `json:"chainId"`
Nonce EthInt `json:"nonce"`
Hash EthHash `json:"hash"`
BlockHash EthHash `json:"blockHash"`
BlockNumber EthInt `json:"blockNumber"`
TransactionIndex EthInt `json:"transacionIndex"`
From EthAddress `json:"from"`
To *EthAddress `json:"to"`
Value EthBigInt `json:"value"`
Type EthInt `json:"type"`
Input EthBytes `json:"input"`
Gas EthInt `json:"gas"`
GasLimit *EthInt `json:"gasLimit,omitempty"`
MaxFeePerGas EthBigInt `json:"maxFeePerGas"`
MaxPriorityFeePerGas EthBigInt `json:"maxPriorityFeePerGas"`
V EthBytes `json:"v"`
R EthBytes `json:"r"`
S EthBytes `json:"s"`
}
type EthTxArgs struct {
ChainID int `json:"chainId"`
Nonce int `json:"nonce"`
To *EthAddress `json:"to"`
Value big.Int `json:"value"`
MaxFeePerGas big.Int `json:"maxFeePerGas"`
MaxPriorityFeePerGas big.Int `json:"maxPrioritiyFeePerGas"`
GasLimit int `json:"gasLimit"`
Input []byte `json:"input"`
V []byte `json:"v"`
R []byte `json:"r"`
S []byte `json:"s"`
}
func NewEthTxArgsFromMessage(msg *types.Message) (EthTxArgs, error) {
var to *EthAddress
params := msg.Params
if msg.To == builtintypes.InitActorAddr {
to = nil
var exec init8.ExecParams
reader := bytes.NewReader(msg.Params)
if err := exec.UnmarshalCBOR(reader); err != nil {
return EthTxArgs{}, err
}
var evmParams evm.ConstructorParams
reader1 := bytes.NewReader(exec.ConstructorParams)
if err := evmParams.UnmarshalCBOR(reader1); err != nil {
return EthTxArgs{}, err
}
params = evmParams.Bytecode
} else {
addr, err := EthAddressFromFilecoinIDAddress(msg.To)
if err != nil {
return EthTxArgs{}, nil
}
to = &addr
}
return EthTxArgs{
ChainID: build.Eip155ChainId,
Nonce: int(msg.Nonce),
To: to,
Value: msg.Value,
Input: params,
MaxFeePerGas: msg.GasFeeCap,
MaxPriorityFeePerGas: msg.GasPremium,
GasLimit: int(msg.GasLimit),
}, nil
}
func (tx *EthTxArgs) ToSignedMessage() (*types.SignedMessage, error) {
from, err := tx.Sender()
if err != nil {
return nil, err
}
var to address.Address
var params []byte
if tx.To == nil && tx.Input == nil {
return nil, fmt.Errorf("to and input cannot both be empty")
}
if tx.To == nil {
// this is a contract creation
to = builtintypes.InitActorAddr
constructorParams, err := actors.SerializeParams(&evm.ConstructorParams{
Bytecode: tx.Input,
InputData: []byte{},
})
if err != nil {
return nil, fmt.Errorf("failed to serialize constructor params: %w", err)
}
evmActorCid, ok := actors.GetActorCodeID(actors.Version8, "evm")
if !ok {
return nil, fmt.Errorf("failed to lookup evm actor code CID")
}
params, err = actors.SerializeParams(&init8.ExecParams{
CodeCID: evmActorCid,
ConstructorParams: constructorParams,
})
if err != nil {
return nil, fmt.Errorf("failed to serialize init actor exec params: %w", err)
}
} else {
addr, err := tx.To.ToFilecoinAddress()
if err != nil {
return nil, err
}
to = addr
params = tx.Input
}
msg := &types.Message{
Nonce: uint64(tx.Nonce),
From: from,
To: to,
Value: tx.Value,
Method: 2,
Params: params,
GasLimit: int64(tx.GasLimit),
GasFeeCap: tx.MaxFeePerGas,
GasPremium: tx.MaxPriorityFeePerGas,
}
sig, err := tx.Signature()
if err != nil {
return nil, err
}
signedMsg := types.SignedMessage{
Message: *msg,
Signature: *sig,
}
return &signedMsg, nil
}
func (tx *EthTxArgs) HashedOriginalRlpMsg() ([]byte, error) {
msg, err := tx.OriginalRlpMsg()
if err != nil {
return nil, err
}
hasher := sha3.NewLegacyKeccak256()
hasher.Write(msg)
hash := hasher.Sum(nil)
return hash, nil
}
func (tx *EthTxArgs) OriginalRlpMsg() ([]byte, error) {
chainId, err := formatInt(tx.ChainID)
if err != nil {
return nil, err
}
nonce, err := formatInt(tx.Nonce)
if err != nil {
return nil, err
}
maxPriorityFeePerGas, err := formatBigInt(tx.MaxPriorityFeePerGas)
if err != nil {
return nil, err
}
maxFeePerGas, err := formatBigInt(tx.MaxFeePerGas)
if err != nil {
return nil, err
}
gasLimit, err := formatInt(tx.GasLimit)
if err != nil {
return nil, err
}
value, err := formatBigInt(tx.Value)
if err != nil {
return nil, err
}
res := []interface{}{
chainId,
nonce,
maxPriorityFeePerGas,
maxFeePerGas,
gasLimit,
formatEthAddr(tx.To),
value,
tx.Input,
[]interface{}{}, // access list
}
encoded, err := EncodeRLP(res)
if err != nil {
return nil, err
}
return append([]byte{0x02}, encoded...), nil
}
func (tx *EthTxArgs) Signature() (*typescrypto.Signature, error) {
if tx.V == nil || tx.R == nil || tx.S == nil {
return nil, fmt.Errorf("one of V, R, or S is nil")
}
sig := append([]byte{}, tx.R...)
sig = append(sig, tx.S...)
sig = append(sig, tx.V...)
if len(sig) != 65 {
return nil, fmt.Errorf("signature is not 65 bytes")
}
return &typescrypto.Signature{
Type: typescrypto.SigTypeDelegated, Data: sig,
}, nil
}
func (tx *EthTxArgs) Sender() (address.Address, error) {
msg, err := tx.OriginalRlpMsg()
if err != nil {
return address.Undef, err
}
hasher := sha3.NewLegacyKeccak256()
hasher.Write(msg)
hash := hasher.Sum(nil)
sig, err := tx.Signature()
if err != nil {
return address.Undef, err
}
pubk, err := gocrypto.EcRecover(hash, sig.Data)
if err != nil {
return address.Undef, err
}
return address.NewSecp256k1Address(pubk)
}
func parseEip1559Tx(data []byte) (*EthTxArgs, error) {
if data[0] != 2 {
return nil, xerrors.Errorf("not an EIP-1559 transaction: first byte is not 2")
}
d, err := DecodeRLP(data[1:])
if err != nil {
return nil, err
}
decoded, ok := d.([]interface{})
if !ok {
return nil, xerrors.Errorf("not an EIP-1559 transaction: decoded data is not a list")
}
if len(decoded) != 9 && len(decoded) != 12 {
return nil, xerrors.Errorf("not an EIP-1559 transaction: should have 6 or 9 elements in the list")
}
chainId, err := parseInt(decoded[0])
if err != nil {
return nil, err
}
nonce, err := parseInt(decoded[1])
if err != nil {
return nil, err
}
maxPriorityFeePerGas, err := parseBigInt(decoded[2])
if err != nil {
return nil, err
}
maxFeePerGas, err := parseBigInt(decoded[3])
if err != nil {
return nil, err
}
gasLimit, err := parseInt(decoded[4])
if err != nil {
return nil, err
}
to, err := parseEthAddr(decoded[5])
if err != nil {
return nil, err
}
value, err := parseBigInt(decoded[6])
if err != nil {
return nil, err
}
input, err := parseBytes(decoded[7])
if err != nil {
return nil, err
}
accessList, ok := decoded[8].([]interface{})
if !ok || (ok && len(accessList) != 0) {
return nil, fmt.Errorf("access list should be an empty list")
}
V, err := parseBytes(decoded[9])
if err != nil {
return nil, err
}
if len(V) == 0 {
V = []byte{0}
}
R, err := parseBytes(decoded[10])
if err != nil {
return nil, err
}
S, err := parseBytes(decoded[11])
if err != nil {
return nil, err
}
args := EthTxArgs{
ChainID: chainId,
Nonce: nonce,
To: to,
MaxPriorityFeePerGas: maxPriorityFeePerGas,
MaxFeePerGas: maxFeePerGas,
GasLimit: gasLimit,
Value: value,
Input: input,
R: padLeadingZeros(R, 32),
S: padLeadingZeros(S, 32),
V: V,
}
return &args, nil
}
func ParseEthTxArgs(data []byte) (*EthTxArgs, error) {
if data[0] > 0x7f {
// legacy transaction
return nil, xerrors.Errorf("legacy transaction is not supported")
} else if data[0] == 1 {
// EIP-2930
return nil, xerrors.Errorf("EIP-2930 transaction is not supported")
} else if data[0] == 2 {
// EIP-1559
return parseEip1559Tx(data)
}
return nil, xerrors.Errorf("unsupported transaction type")
}
func padLeadingZeros(data []byte, length int) []byte {
if len(data) >= length {
return data
}
zeros := make([]byte, length-len(data))
return append(zeros, data...)
}
func removeLeadingZeros(data []byte) []byte {
firstNonZeroIndex := len(data)
for i, b := range data {
if b > 0 {
firstNonZeroIndex = i
break
}
}
return data[firstNonZeroIndex:]
}
func formatInt(val int) ([]byte, error) {
buf := new(bytes.Buffer)
err := binary.Write(buf, binary.BigEndian, int64(val))
if err != nil {
return nil, err
}
return removeLeadingZeros(buf.Bytes()), nil
}
func formatEthAddr(addr *EthAddress) []byte {
if addr == nil {
return nil
}
return addr[:]
}
func formatBigInt(val big.Int) ([]byte, error) {
b, err := val.Bytes()
if err != nil {
return nil, err
}
return removeLeadingZeros(b), nil
}
func parseInt(v interface{}) (int, error) {
data, ok := v.([]byte)
if !ok {
return 0, xerrors.Errorf("cannot parse interface to int: input is not a byte array")
}
if len(data) == 0 {
return 0, nil
}
if len(data) > 8 {
return 0, xerrors.Errorf("cannot parse interface to int: length is more than 8 bytes")
}
var value int64
r := bytes.NewReader(append(make([]byte, 8-len(data)), data...))
if err := binary.Read(r, binary.BigEndian, &value); err != nil {
return 0, xerrors.Errorf("cannot parse interface to EthInt: %w", err)
}
return int(value), nil
}
func parseBigInt(v interface{}) (big.Int, error) {
data, ok := v.([]byte)
if !ok {
return big.Zero(), xerrors.Errorf("cannot parse interface to big.Int: input is not a byte array")
}
if len(data) == 0 {
return big.Zero(), nil
}
var b mathbig.Int
b.SetBytes(data)
return big.NewFromGo(&b), nil
}
func parseBytes(v interface{}) ([]byte, error) {
val, ok := v.([]byte)
if !ok {
return nil, xerrors.Errorf("cannot parse interface into bytes: input is not a byte array")
}
return val, nil
}
func parseEthAddr(v interface{}) (*EthAddress, error) {
b, err := parseBytes(v)
if err != nil {
return nil, err
}
if b == nil || len(b) == 0 {
return nil, nil
}
addr, err := EthAddressFromBytes(b)
if err != nil {
return nil, err
}
return &addr, nil
}

File diff suppressed because one or more lines are too long

View File

@ -24,6 +24,9 @@ import (
type EthInt int64
func (e EthInt) MarshalJSON() ([]byte, error) {
if e == 0 {
return json.Marshal("0x0")
}
return json.Marshal(fmt.Sprintf("0x%x", e))
}
@ -75,8 +78,14 @@ func (e *EthBigInt) UnmarshalJSON(b []byte) error {
type EthBytes []byte
func (e EthBytes) MarshalJSON() ([]byte, error) {
encoded := "0x" + hex.EncodeToString(e)
return json.Marshal(encoded)
if len(e) == 0 {
return json.Marshal("0x00")
}
s := hex.EncodeToString(e)
if len(s)%2 == 1 {
s = "0" + s
}
return json.Marshal("0x" + s)
}
func (e *EthBytes) UnmarshalJSON(b []byte) error {
@ -144,33 +153,13 @@ func NewEthBlock() EthBlock {
}
}
type EthTx struct {
ChainID EthInt `json:"chainId"`
Nonce uint64 `json:"nonce"`
Hash EthHash `json:"hash"`
BlockHash EthHash `json:"blockHash"`
BlockNumber EthInt `json:"blockNumber"`
TransactionIndex EthInt `json:"transacionIndex"`
From EthAddress `json:"from"`
To *EthAddress `json:"to"`
Value EthBigInt `json:"value"`
Type EthInt `json:"type"`
Input EthBytes `json:"input"`
Gas EthInt `json:"gas"`
MaxFeePerGas EthBigInt `json:"maxFeePerGas"`
MaxPriorityFeePerGas EthBigInt `json:"maxPriorityFeePerGas"`
V EthBigInt `json:"v"`
R EthBigInt `json:"r"`
S EthBigInt `json:"s"`
}
type EthCall struct {
From EthAddress `json:"from"`
To EthAddress `json:"to"`
Gas EthInt `json:"gas"`
GasPrice EthBigInt `json:"gasPrice"`
Value EthBigInt `json:"value"`
Data EthBytes `json:"data"`
From EthAddress `json:"from"`
To *EthAddress `json:"to"`
Gas EthInt `json:"gas"`
GasPrice EthBigInt `json:"gasPrice"`
Value EthBigInt `json:"value"`
Data EthBytes `json:"data"`
}
func (c *EthCall) UnmarshalJSON(b []byte) error {
@ -186,7 +175,7 @@ func (c *EthCall) UnmarshalJSON(b []byte) error {
type EthTxReceipt struct {
TransactionHash EthHash `json:"transactionHash"`
TransactionIndex EthInt `json:"transacionIndex"`
TransactionIndex EthInt `json:"transactionIndex"`
BlockHash EthHash `json:"blockHash"`
BlockNumber EthInt `json:"blockNumber"`
From EthAddress `json:"from"`
@ -199,6 +188,8 @@ type EthTxReceipt struct {
CumulativeGasUsed EthInt `json:"cumulativeGasUsed"`
GasUsed EthInt `json:"gasUsed"`
EffectiveGasPrice EthBigInt `json:"effectiveGasPrice"`
LogsBloom EthBytes `json:"logsBloom"`
Logs []string `json:"logs"`
}
func NewEthTxReceipt(tx EthTx, lookup *MsgLookup, replay *InvocResult) (EthTxReceipt, error) {
@ -210,6 +201,8 @@ func NewEthTxReceipt(tx EthTx, lookup *MsgLookup, replay *InvocResult) (EthTxRec
From: tx.From,
To: tx.To,
StateRoot: EmptyEthHash,
LogsBloom: []byte{0},
Logs: []string{},
}
contractAddr, err := CheckContractCreation(lookup)
@ -318,6 +311,15 @@ func EthAddressFromHex(s string) (EthAddress, error) {
return h, nil
}
func EthAddressFromBytes(b []byte) (EthAddress, error) {
var a EthAddress
if len(b) != ETH_ADDRESS_LENGTH {
return EthAddress{}, xerrors.Errorf("cannot initiate a new EthAddress: incorrect input length")
}
copy(a[:], b[:])
return a, nil
}
type EthHash [ETH_HASH_LENGTH]byte
func (h EthHash) MarshalJSON() ([]byte, error) {

View File

@ -1,4 +1,4 @@
//stm: #unit
// stm: #unit
package api
import (

View File

@ -982,18 +982,18 @@ func (mr *MockFullNodeMockRecorder) EthChainId(arg0 interface{}) *gomock.Call {
}
// EthEstimateGas mocks base method.
func (m *MockFullNode) EthEstimateGas(arg0 context.Context, arg1 api.EthCall, arg2 string) (api.EthInt, error) {
func (m *MockFullNode) EthEstimateGas(arg0 context.Context, arg1 api.EthCall) (api.EthInt, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthEstimateGas", arg0, arg1, arg2)
ret := m.ctrl.Call(m, "EthEstimateGas", arg0, arg1)
ret0, _ := ret[0].(api.EthInt)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// EthEstimateGas indicates an expected call of EthEstimateGas.
func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1, arg2 interface{}) *gomock.Call {
func (mr *MockFullNodeMockRecorder) EthEstimateGas(arg0, arg1 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthEstimateGas", reflect.TypeOf((*MockFullNode)(nil).EthEstimateGas), arg0, arg1, arg2)
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "EthEstimateGas", reflect.TypeOf((*MockFullNode)(nil).EthEstimateGas), arg0, arg1)
}
// EthGasPrice mocks base method.
@ -1042,7 +1042,7 @@ func (mr *MockFullNodeMockRecorder) EthGetBlockByHash(arg0, arg1, arg2 interface
}
// EthGetBlockByNumber mocks base method.
func (m *MockFullNode) EthGetBlockByNumber(arg0 context.Context, arg1 api.EthInt, arg2 bool) (api.EthBlock, error) {
func (m *MockFullNode) EthGetBlockByNumber(arg0 context.Context, arg1 string, arg2 bool) (api.EthBlock, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthGetBlockByNumber", arg0, arg1, arg2)
ret0, _ := ret[0].(api.EthBlock)
@ -1147,10 +1147,10 @@ func (mr *MockFullNodeMockRecorder) EthGetTransactionByBlockNumberAndIndex(arg0,
}
// EthGetTransactionByHash mocks base method.
func (m *MockFullNode) EthGetTransactionByHash(arg0 context.Context, arg1 api.EthHash) (api.EthTx, error) {
func (m *MockFullNode) EthGetTransactionByHash(arg0 context.Context, arg1 *api.EthHash) (*api.EthTx, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthGetTransactionByHash", arg0, arg1)
ret0, _ := ret[0].(api.EthTx)
ret0, _ := ret[0].(*api.EthTx)
ret1, _ := ret[1].(error)
return ret0, ret1
}
@ -1177,10 +1177,10 @@ func (mr *MockFullNodeMockRecorder) EthGetTransactionCount(arg0, arg1, arg2 inte
}
// EthGetTransactionReceipt mocks base method.
func (m *MockFullNode) EthGetTransactionReceipt(arg0 context.Context, arg1 api.EthHash) (api.EthTxReceipt, error) {
func (m *MockFullNode) EthGetTransactionReceipt(arg0 context.Context, arg1 api.EthHash) (*api.EthTxReceipt, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "EthGetTransactionReceipt", arg0, arg1)
ret0, _ := ret[0].(api.EthTxReceipt)
ret0, _ := ret[0].(*api.EthTxReceipt)
ret1, _ := ret[1].(error)
return ret0, ret1
}

View File

@ -10,12 +10,15 @@ import (
"github.com/google/uuid"
blocks "github.com/ipfs/go-block-format"
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/metrics"
"github.com/libp2p/go-libp2p/core/network"
"github.com/libp2p/go-libp2p/core/peer"
"github.com/libp2p/go-libp2p/core/protocol"
"golang.org/x/xerrors"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-bitfield"
datatransfer "github.com/filecoin-project/go-data-transfer"
@ -37,6 +40,7 @@ import (
lminer "github.com/filecoin-project/lotus/chain/actors/builtin/miner"
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/journal/alerting"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
"github.com/filecoin-project/lotus/node/modules/dtypes"
"github.com/filecoin-project/lotus/node/repo/imports"
"github.com/filecoin-project/lotus/storage/pipeline/sealiface"
@ -226,7 +230,7 @@ type FullNodeStruct struct {
EthChainId func(p0 context.Context) (EthInt, error) `perm:"read"`
EthEstimateGas func(p0 context.Context, p1 EthCall, p2 string) (EthInt, error) `perm:"read"`
EthEstimateGas func(p0 context.Context, p1 EthCall) (EthInt, error) `perm:"read"`
EthGasPrice func(p0 context.Context) (EthBigInt, error) `perm:"read"`
@ -234,7 +238,7 @@ type FullNodeStruct struct {
EthGetBlockByHash func(p0 context.Context, p1 EthHash, p2 bool) (EthBlock, error) `perm:"read"`
EthGetBlockByNumber func(p0 context.Context, p1 EthInt, p2 bool) (EthBlock, error) `perm:"read"`
EthGetBlockByNumber func(p0 context.Context, p1 string, p2 bool) (EthBlock, error) `perm:"read"`
EthGetBlockTransactionCountByHash func(p0 context.Context, p1 EthHash) (EthInt, error) `perm:"read"`
@ -248,11 +252,11 @@ type FullNodeStruct struct {
EthGetTransactionByBlockNumberAndIndex func(p0 context.Context, p1 EthInt, p2 EthInt) (EthTx, error) `perm:"read"`
EthGetTransactionByHash func(p0 context.Context, p1 EthHash) (EthTx, error) `perm:"read"`
EthGetTransactionByHash func(p0 context.Context, p1 *EthHash) (*EthTx, error) `perm:"read"`
EthGetTransactionCount func(p0 context.Context, p1 EthAddress, p2 string) (EthInt, error) `perm:"read"`
EthGetTransactionReceipt func(p0 context.Context, p1 EthHash) (EthTxReceipt, error) `perm:"read"`
EthGetTransactionReceipt func(p0 context.Context, p1 EthHash) (*EthTxReceipt, error) `perm:"read"`
EthMaxPriorityFeePerGas func(p0 context.Context) (EthBigInt, error) `perm:"read"`
@ -1881,14 +1885,14 @@ func (s *FullNodeStub) EthChainId(p0 context.Context) (EthInt, error) {
return *new(EthInt), ErrNotSupported
}
func (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 EthCall, p2 string) (EthInt, error) {
func (s *FullNodeStruct) EthEstimateGas(p0 context.Context, p1 EthCall) (EthInt, error) {
if s.Internal.EthEstimateGas == nil {
return *new(EthInt), ErrNotSupported
}
return s.Internal.EthEstimateGas(p0, p1, p2)
return s.Internal.EthEstimateGas(p0, p1)
}
func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 EthCall, p2 string) (EthInt, error) {
func (s *FullNodeStub) EthEstimateGas(p0 context.Context, p1 EthCall) (EthInt, error) {
return *new(EthInt), ErrNotSupported
}
@ -1925,14 +1929,14 @@ func (s *FullNodeStub) EthGetBlockByHash(p0 context.Context, p1 EthHash, p2 bool
return *new(EthBlock), ErrNotSupported
}
func (s *FullNodeStruct) EthGetBlockByNumber(p0 context.Context, p1 EthInt, p2 bool) (EthBlock, error) {
func (s *FullNodeStruct) EthGetBlockByNumber(p0 context.Context, p1 string, p2 bool) (EthBlock, error) {
if s.Internal.EthGetBlockByNumber == nil {
return *new(EthBlock), ErrNotSupported
}
return s.Internal.EthGetBlockByNumber(p0, p1, p2)
}
func (s *FullNodeStub) EthGetBlockByNumber(p0 context.Context, p1 EthInt, p2 bool) (EthBlock, error) {
func (s *FullNodeStub) EthGetBlockByNumber(p0 context.Context, p1 string, p2 bool) (EthBlock, error) {
return *new(EthBlock), ErrNotSupported
}
@ -2002,15 +2006,15 @@ func (s *FullNodeStub) EthGetTransactionByBlockNumberAndIndex(p0 context.Context
return *new(EthTx), ErrNotSupported
}
func (s *FullNodeStruct) EthGetTransactionByHash(p0 context.Context, p1 EthHash) (EthTx, error) {
func (s *FullNodeStruct) EthGetTransactionByHash(p0 context.Context, p1 *EthHash) (*EthTx, error) {
if s.Internal.EthGetTransactionByHash == nil {
return *new(EthTx), ErrNotSupported
return nil, ErrNotSupported
}
return s.Internal.EthGetTransactionByHash(p0, p1)
}
func (s *FullNodeStub) EthGetTransactionByHash(p0 context.Context, p1 EthHash) (EthTx, error) {
return *new(EthTx), ErrNotSupported
func (s *FullNodeStub) EthGetTransactionByHash(p0 context.Context, p1 *EthHash) (*EthTx, error) {
return nil, ErrNotSupported
}
func (s *FullNodeStruct) EthGetTransactionCount(p0 context.Context, p1 EthAddress, p2 string) (EthInt, error) {
@ -2024,15 +2028,15 @@ func (s *FullNodeStub) EthGetTransactionCount(p0 context.Context, p1 EthAddress,
return *new(EthInt), ErrNotSupported
}
func (s *FullNodeStruct) EthGetTransactionReceipt(p0 context.Context, p1 EthHash) (EthTxReceipt, error) {
func (s *FullNodeStruct) EthGetTransactionReceipt(p0 context.Context, p1 EthHash) (*EthTxReceipt, error) {
if s.Internal.EthGetTransactionReceipt == nil {
return *new(EthTxReceipt), ErrNotSupported
return nil, ErrNotSupported
}
return s.Internal.EthGetTransactionReceipt(p0, p1)
}
func (s *FullNodeStub) EthGetTransactionReceipt(p0 context.Context, p1 EthHash) (EthTxReceipt, error) {
return *new(EthTxReceipt), ErrNotSupported
func (s *FullNodeStub) EthGetTransactionReceipt(p0 context.Context, p1 EthHash) (*EthTxReceipt, error) {
return nil, ErrNotSupported
}
func (s *FullNodeStruct) EthMaxPriorityFeePerGas(p0 context.Context) (EthBigInt, error) {

176
api/rlp.go Normal file
View File

@ -0,0 +1,176 @@
package api
import (
"bytes"
"encoding/binary"
"fmt"
"golang.org/x/xerrors"
)
func EncodeRLP(val interface{}) ([]byte, error) {
return encodeRLP(val)
}
func encodeRLPListItems(list []interface{}) (result []byte, err error) {
res := []byte{}
for _, elem := range list {
encoded, err := encodeRLP(elem)
if err != nil {
return nil, err
}
res = append(res, encoded...)
}
return res, nil
}
func encodeLength(length int) (lenInBytes []byte, err error) {
if length == 0 {
return nil, fmt.Errorf("cannot encode length: length should be larger than 0")
}
buf := new(bytes.Buffer)
err = binary.Write(buf, binary.BigEndian, int64(length))
if err != nil {
return nil, err
}
firstNonZeroIndex := len(buf.Bytes()) - 1
for i, b := range buf.Bytes() {
if b != 0 {
firstNonZeroIndex = i
break
}
}
res := buf.Bytes()[firstNonZeroIndex:]
return res, nil
}
func encodeRLP(val interface{}) ([]byte, error) {
if data, ok := val.([]byte); ok {
if len(data) == 1 && data[0] <= 0x7f {
return data, nil
} else if len(data) <= 55 {
prefix := byte(0x80 + len(data))
return append([]byte{prefix}, data...), nil
} else {
lenInBytes, err := encodeLength(len(data))
if err != nil {
return nil, err
}
prefix := byte(0xb7 + len(lenInBytes))
return append(
[]byte{prefix},
append(lenInBytes, data...)...,
), nil
}
} else if data, ok := val.([]interface{}); ok {
encodedList, err := encodeRLPListItems(data)
if err != nil {
return nil, err
}
if len(encodedList) <= 55 {
prefix := byte(0xc0 + len(encodedList))
return append(
[]byte{prefix},
encodedList...,
), nil
}
lenInBytes, err := encodeLength(len(encodedList))
if err != nil {
return nil, err
}
prefix := byte(0xf7 + len(lenInBytes))
return append(
[]byte{prefix},
append(lenInBytes, encodedList...)...,
), nil
}
return nil, fmt.Errorf("input data should either be a list or a byte array")
}
func DecodeRLP(data []byte) (interface{}, error) {
res, consumed, err := decodeRLP(data)
if err != nil {
return nil, err
}
if consumed != len(data) {
return nil, xerrors.Errorf("invalid rlp data: length %d, consumed %d", len(data), consumed)
}
return res, nil
}
func decodeRLP(data []byte) (res interface{}, consumed int, err error) {
if len(data) == 0 {
return data, 0, xerrors.Errorf("invalid rlp data: data cannot be empty")
}
if data[0] >= 0xf8 {
listLenInBytes := int(data[0]) - 0xf7
listLen, err := decodeLength(data[1:], listLenInBytes)
if err != nil {
return nil, 0, err
}
if 1+listLenInBytes+listLen > len(data) {
return nil, 0, xerrors.Errorf("invalid rlp data: out of bound while parsing list")
}
result, err := decodeListElems(data[1+listLenInBytes:], listLen)
return result, 1 + listLenInBytes + listLen, err
} else if data[0] >= 0xc0 {
length := int(data[0]) - 0xc0
result, err := decodeListElems(data[1:], length)
return result, 1 + length, err
} else if data[0] >= 0xb8 {
strLenInBytes := int(data[0]) - 0xb7
strLen, err := decodeLength(data[1:], strLenInBytes)
if err != nil {
return nil, 0, err
}
totalLen := 1 + strLenInBytes + strLen
if totalLen > len(data) {
return nil, 0, xerrors.Errorf("invalid rlp data: out of bound while parsing string")
}
return data[1+strLenInBytes : totalLen], totalLen, nil
} else if data[0] >= 0x80 {
length := int(data[0]) - 0x80
if 1+length > len(data) {
return nil, 0, xerrors.Errorf("invalid rlp data: out of bound while parsing string")
}
return data[1 : 1+length], 1 + length, nil
}
return []byte{data[0]}, 1, nil
}
func decodeLength(data []byte, lenInBytes int) (length int, err error) {
if lenInBytes > len(data) || lenInBytes > 8 {
return 0, xerrors.Errorf("invalid rlp data: out of bound while parsing list length")
}
var decodedLength int64
r := bytes.NewReader(append(make([]byte, 8-lenInBytes), data[:lenInBytes]...))
if err := binary.Read(r, binary.BigEndian, &decodedLength); err != nil {
return 0, xerrors.Errorf("invalid rlp data: cannot parse string length: %w", err)
}
if lenInBytes+int(decodedLength) > len(data) {
return 0, xerrors.Errorf("invalid rlp data: out of bound while parsing list")
}
return int(decodedLength), nil
}
func decodeListElems(data []byte, length int) (res []interface{}, err error) {
totalConsumed := 0
result := []interface{}{}
// set a limit to make sure it doesn't loop infinitely
for i := 0; totalConsumed < length && i < 5000; i++ {
elem, consumed, err := decodeRLP(data[totalConsumed:])
if err != nil {
return nil, xerrors.Errorf("invalid rlp data: cannot decode list element: %w", err)
}
totalConsumed += consumed
result = append(result, elem)
}
if totalConsumed != length {
return nil, xerrors.Errorf("invalid rlp data: incorrect list length")
}
return result, nil
}

175
api/rlp_test.go Normal file
View File

@ -0,0 +1,175 @@
package api
import (
"encoding/hex"
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/require"
)
func TestEncode(t *testing.T) {
testcases := []TestCase{
{[]byte(""), mustDecodeHex("0x80")},
{mustDecodeHex("0x01"), mustDecodeHex("0x01")},
{mustDecodeHex("0xaa"), mustDecodeHex("0x81aa")},
{mustDecodeHex("0x0402"), mustDecodeHex("0x820402")},
{
[]interface{}{},
mustDecodeHex("0xc0"),
},
{
mustDecodeHex("0xabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"),
mustDecodeHex("0xb83cabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"),
},
{
mustDecodeHex("0xabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"),
mustDecodeHex("0xb8aaabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"),
},
{
[]interface{}{
mustDecodeHex("0xaaaa"),
mustDecodeHex("0xbbbb"),
mustDecodeHex("0xcccc"),
mustDecodeHex("0xdddd"),
},
mustDecodeHex("0xcc82aaaa82bbbb82cccc82dddd"),
},
{
[]interface{}{
mustDecodeHex("0xaaaaaaaaaaaaaaaaaaaa"),
mustDecodeHex("0xbbbbbbbbbbbbbbbbbbbb"),
[]interface{}{
mustDecodeHex("0xc1c1c1c1c1c1c1c1c1c1"),
mustDecodeHex("0xc2c2c2c2c2c2c2c2c2c2"),
mustDecodeHex("0xc3c3c3c3c3c3c3c3c3c3"),
},
mustDecodeHex("0xdddddddddddddddddddd"),
mustDecodeHex("0xeeeeeeeeeeeeeeeeeeee"),
mustDecodeHex("0xffffffffffffffffffff"),
},
mustDecodeHex("0xf8598aaaaaaaaaaaaaaaaaaaaa8abbbbbbbbbbbbbbbbbbbbe18ac1c1c1c1c1c1c1c1c1c18ac2c2c2c2c2c2c2c2c2c28ac3c3c3c3c3c3c3c3c3c38adddddddddddddddddddd8aeeeeeeeeeeeeeeeeeeee8affffffffffffffffffff"),
},
}
for _, tc := range testcases {
result, err := EncodeRLP(tc.Input)
require.Nil(t, err)
require.Equal(t, tc.Output.([]byte), result)
}
}
func TestDecodeString(t *testing.T) {
testcases := []TestCase{
{"0x00", "0x00"},
{"0x80", "0x"},
{"0x0f", "0x0f"},
{"0x81aa", "0xaa"},
{"0x820400", "0x0400"},
{"0xb83cabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd",
"0xabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcdabcd"},
}
for _, tc := range testcases {
input, err := hex.DecodeString(strings.Replace(tc.Input.(string), "0x", "", -1))
require.Nil(t, err)
output, err := hex.DecodeString(strings.Replace(tc.Output.(string), "0x", "", -1))
require.Nil(t, err)
result, err := DecodeRLP(input)
require.Nil(t, err)
require.Equal(t, output, result.([]byte))
}
}
func mustDecodeHex(s string) []byte {
d, err := hex.DecodeString(strings.Replace(s, "0x", "", -1))
if err != nil {
panic(fmt.Errorf("err must be nil: %w", err))
}
return d
}
func TestDecodeList(t *testing.T) {
testcases := []TestCase{
{"0xc0", []interface{}{}},
{"0xc100", []interface{}{[]byte{0}}},
{"0xc3000102", []interface{}{[]byte{0}, []byte{1}, []byte{2}}},
{"0xc4000181aa", []interface{}{[]byte{0}, []byte{1}, []byte{0xaa}}},
{"0xc6000181aa81ff", []interface{}{[]byte{0}, []byte{1}, []byte{0xaa}, []byte{0xff}}},
{"0xf8428aabcdabcdabcdabcdabcd8aabcdabcdabcdabcdabcd8aabcdabcdabcdabcdabcd8aabcdabcdabcdabcdabcd8aabcdabcdabcdabcdabcd8aabcdabcdabcdabcdabcd",
[]interface{}{
mustDecodeHex("0xabcdabcdabcdabcdabcd"),
mustDecodeHex("0xabcdabcdabcdabcdabcd"),
mustDecodeHex("0xabcdabcdabcdabcdabcd"),
mustDecodeHex("0xabcdabcdabcdabcdabcd"),
mustDecodeHex("0xabcdabcdabcdabcdabcd"),
mustDecodeHex("0xabcdabcdabcdabcdabcd"),
},
},
{"0xf1030185012a05f2008504a817c800825208942b87d1cb599bc2a606db9a0169fcec96af04ad3a880de0b6b3a764000080c0",
[]interface{}{
[]byte{3},
[]byte{1},
mustDecodeHex("0x012a05f200"),
mustDecodeHex("0x04a817c800"),
mustDecodeHex("0x5208"),
mustDecodeHex("0x2b87d1CB599Bc2a606Db9A0169fcEc96Af04ad3a"),
mustDecodeHex("0x0de0b6b3a7640000"),
[]byte{},
[]interface{}{},
}},
}
for _, tc := range testcases {
input, err := hex.DecodeString(strings.Replace(tc.Input.(string), "0x", "", -1))
require.Nil(t, err)
result, err := DecodeRLP(input)
require.Nil(t, err)
fmt.Println(result)
r := result.([]interface{})
require.Equal(t, len(tc.Output.([]interface{})), len(r))
for i, v := range r {
require.Equal(t, tc.Output.([]interface{})[i], v)
}
}
}
func TestDecodeEncodeTx(t *testing.T) {
testcases := [][]byte{
mustDecodeHex("0xdc82013a0185012a05f2008504a817c8008080872386f26fc1000000c0"),
mustDecodeHex("0xf85f82013a0185012a05f2008504a817c8008080872386f26fc1000000c001a027fa36fb9623e4d71fcdd7f7dce71eb814c9560dcf3908c1719386e2efd122fba05fb4e4227174eeb0ba84747a4fb883c8d4e0fdb129c4b1f42e90282c41480234"),
mustDecodeHex("0xf9061c82013a0185012a05f2008504a817c8008080872386f26fc10000b905bb608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610556806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101939190610496565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104ca565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561048b5761048a61040d565b5b828202905092915050565b60006104a182610337565b91506104ac83610337565b9250828210156104bf576104be61040d565b5b828203905092915050565b60006104d582610337565b91506104e083610337565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156105155761051461040d565b5b82820190509291505056fea26469706673582212208e5b4b874c839967f88008ed2fa42d6c2d9c9b0ae05d1d2c61faa7d229c134e664736f6c634300080d0033c080a0c4e9477f57c6848b2f1ea73a14809c1f44529d20763c947f3ac8ffd3d1629d93a011485a215457579bb13ac7b53bb9d6804763ae6fe5ce8ddd41642cea55c9a09a"),
mustDecodeHex("0xf9063082013a0185012a05f2008504a817c8008094025b594a4f1c4888cafcfaf2bb24ed95507749e0872386f26fc10000b905bb608060405234801561001057600080fd5b506127106000803273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550610556806100656000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c80637bd703e81461004657806390b98a1114610076578063f8b2cb4f146100a6575b600080fd5b610060600480360381019061005b919061030a565b6100d6565b60405161006d9190610350565b60405180910390f35b610090600480360381019061008b9190610397565b6100f4565b60405161009d91906103f2565b60405180910390f35b6100c060048036038101906100bb919061030a565b61025f565b6040516100cd9190610350565b60405180910390f35b600060026100e38361025f565b6100ed919061043c565b9050919050565b6000816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410156101455760009050610259565b816000803373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101939190610496565b92505081905550816000808573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008282546101e891906104ca565b925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef8460405161024c9190610350565b60405180910390a3600190505b92915050565b60008060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006102d7826102ac565b9050919050565b6102e7816102cc565b81146102f257600080fd5b50565b600081359050610304816102de565b92915050565b6000602082840312156103205761031f6102a7565b5b600061032e848285016102f5565b91505092915050565b6000819050919050565b61034a81610337565b82525050565b60006020820190506103656000830184610341565b92915050565b61037481610337565b811461037f57600080fd5b50565b6000813590506103918161036b565b92915050565b600080604083850312156103ae576103ad6102a7565b5b60006103bc858286016102f5565b92505060206103cd85828601610382565b9150509250929050565b60008115159050919050565b6103ec816103d7565b82525050565b600060208201905061040760008301846103e3565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061044782610337565b915061045283610337565b9250817fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff048311821515161561048b5761048a61040d565b5b828202905092915050565b60006104a182610337565b91506104ac83610337565b9250828210156104bf576104be61040d565b5b828203905092915050565b60006104d582610337565b91506104e083610337565b9250827fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff038211156105155761051461040d565b5b82820190509291505056fea26469706673582212208e5b4b874c839967f88008ed2fa42d6c2d9c9b0ae05d1d2c61faa7d229c134e664736f6c634300080d0033c080a0fe38720928596f9e9dfbf891d00311638efce3713f03cdd67b212ecbbcf18f29a05993e656c0b35b8a580da6aff7c89b3d3e8b1c6f83a7ce09473c0699a8500b9c"),
}
for _, tc := range testcases {
decoded, err := DecodeRLP(tc)
require.Nil(t, err)
encoded, err := EncodeRLP(decoded)
require.Nil(t, err)
require.Equal(t, tc, encoded)
}
}
func TestDecodeError(t *testing.T) {
testcases := [][]byte{
mustDecodeHex("0xdc82013a0185012a05f2008504a817c8008080872386f26fc1000000"),
mustDecodeHex("0xdc013a01012a05f2008504a817c8008080872386f26fc1000000"),
mustDecodeHex("0xdc82013a0185012a05f28504a817c08080872386f26fc1000000"),
mustDecodeHex("0xdc82013a0185012a05f504a817c080872386ffc1000000"),
mustDecodeHex("0x013a018505f2008504a817c8008080872386f26fc1000000"),
}
for _, tc := range testcases {
_, err := DecodeRLP(tc)
require.NotNil(t, err, hex.EncodeToString(tc))
}
}

View File

@ -26,6 +26,7 @@ import (
blockadt "github.com/filecoin-project/specs-actors/actors/util/adt"
"github.com/filecoin-project/specs-actors/v7/actors/runtime/proof"
"github.com/filecoin-project/lotus/api"
bstore "github.com/filecoin-project/lotus/blockstore"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain"
@ -543,7 +544,7 @@ func (filec *FilecoinEC) checkBlockMessages(ctx context.Context, b *types.FullBl
smArr := blockadt.MakeEmptyArray(tmpstore)
for i, m := range b.SecpkMessages {
if filec.sm.GetNetworkVersion(ctx, b.Header.Height) >= network.Version14 {
if m.Signature.Type != crypto.SigTypeSecp256k1 {
if m.Signature.Type != crypto.SigTypeSecp256k1 && m.Signature.Type != crypto.SigTypeDelegated {
return xerrors.Errorf("block had invalid secpk message at index %d: %w", i, err)
}
}
@ -559,7 +560,20 @@ func (filec *FilecoinEC) checkBlockMessages(ctx context.Context, b *types.FullBl
return xerrors.Errorf("failed to resolve key addr: %w", err)
}
if err := sigs.Verify(&m.Signature, kaddr, m.Message.Cid().Bytes()); err != nil {
digest := m.Message.Cid().Bytes()
if m.Signature.Type == crypto.SigTypeDelegated {
txArgs, err := api.NewEthTxArgsFromMessage(&m.Message)
if err != nil {
return err
}
msg, err := txArgs.OriginalRlpMsg()
if err != nil {
return err
}
digest = msg
}
if err := sigs.Verify(&m.Signature, kaddr, digest); err != nil {
return xerrors.Errorf("secpk message %s has invalid signature: %w", m.Cid(), err)
}

View File

@ -65,7 +65,7 @@ func (filec *FilecoinEC) CreateBlock(ctx context.Context, w api.Wallet, bt *api.
}
blsMsgCids = append(blsMsgCids, c)
} else if msg.Signature.Type == crypto.SigTypeSecp256k1 {
} else if msg.Signature.Type == crypto.SigTypeSecp256k1 || msg.Signature.Type == crypto.SigTypeDelegated {
c, err := filec.sm.ChainStore().PutMessage(ctx, msg)
if err != nil {
return nil, err

View File

@ -3,6 +3,7 @@ package messagepool
import (
"bytes"
"context"
"encoding/hex"
"errors"
"fmt"
"math"
@ -770,6 +771,16 @@ func sigCacheKey(m *types.SignedMessage) (string, error) {
return string(hashCache[:]), nil
case crypto.SigTypeSecp256k1:
return string(m.Cid().Bytes()), nil
case crypto.SigTypeDelegated:
txArgs, err := api.NewEthTxArgsFromMessage(&m.Message)
if err != nil {
return "", err
}
msg, err := txArgs.HashedOriginalRlpMsg()
if err != nil {
return "", err
}
return hex.EncodeToString(msg), nil
default:
return "", xerrors.Errorf("unrecognized signature type: %d", m.Signature.Type)
}
@ -787,7 +798,19 @@ func (mp *MessagePool) VerifyMsgSig(m *types.SignedMessage) error {
return nil
}
if err := sigs.Verify(&m.Signature, m.Message.From, m.Message.Cid().Bytes()); err != nil {
if m.Signature.Type == crypto.SigTypeDelegated {
txArgs, err := api.NewEthTxArgsFromMessage(&m.Message)
if err != nil {
return err
}
msg, err := txArgs.OriginalRlpMsg()
if err != nil {
return err
}
if err := sigs.Verify(&m.Signature, m.Message.From, msg); err != nil {
return err
}
} else if err := sigs.Verify(&m.Signature, m.Message.From, m.Message.Cid().Bytes()); err != nil {
return err
}

View File

@ -97,7 +97,7 @@ func (sm *selectedMessages) tryToAdd(mc *msgChain) bool {
sm.msgs = append(sm.msgs, mc.msgs...)
sm.blsLimit -= l
sm.gasLimit -= mc.gasLimit
} else if mc.sigType == crypto.SigTypeSecp256k1 {
} else if mc.sigType == crypto.SigTypeSecp256k1 || mc.sigType == crypto.SigTypeDelegated {
if sm.secpLimit < l {
return false
}
@ -123,7 +123,7 @@ func (sm *selectedMessages) tryToAddWithDeps(mc *msgChain, mp *MessagePool, base
if mc.sigType == crypto.SigTypeBLS {
smMsgLimit = sm.blsLimit
} else if mc.sigType == crypto.SigTypeSecp256k1 {
} else if mc.sigType == crypto.SigTypeSecp256k1 || mc.sigType == crypto.SigTypeDelegated {
smMsgLimit = sm.secpLimit
} else {
return false
@ -174,7 +174,7 @@ func (sm *selectedMessages) tryToAddWithDeps(mc *msgChain, mp *MessagePool, base
if mc.sigType == crypto.SigTypeBLS {
sm.blsLimit -= chainMsgLimit
} else if mc.sigType == crypto.SigTypeSecp256k1 {
} else if mc.sigType == crypto.SigTypeSecp256k1 || mc.sigType == crypto.SigTypeDelegated {
sm.secpLimit -= chainMsgLimit
}
@ -187,7 +187,7 @@ func (sm *selectedMessages) trimChain(mc *msgChain, mp *MessagePool, baseFee typ
if msgLimit > sm.blsLimit {
msgLimit = sm.blsLimit
}
} else if mc.sigType == crypto.SigTypeSecp256k1 {
} else if mc.sigType == crypto.SigTypeSecp256k1 || mc.sigType == crypto.SigTypeDelegated {
if msgLimit > sm.secpLimit {
msgLimit = sm.secpLimit
}

View File

@ -31,6 +31,7 @@ import (
"github.com/filecoin-project/lotus/chain/types/mock"
"github.com/filecoin-project/lotus/chain/wallet"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
)

View File

@ -1145,7 +1145,7 @@ func persistMessages(ctx context.Context, bs bstore.Blockstore, bst *exchange.Co
}
}
for _, m := range bst.Secpk {
if m.Signature.Type != crypto.SigTypeSecp256k1 {
if m.Signature.Type != crypto.SigTypeSecp256k1 && m.Signature.Type != crypto.SigTypeDelegated {
return xerrors.Errorf("unknown signature type on message %s: %q", m.Cid(), m.Signature.Type)
}
//log.Infof("putting secp256k1 message: %s", m.Cid())

View File

@ -39,6 +39,8 @@ func (kt *KeyType) UnmarshalJSON(bb []byte) error {
*kt = KTBLS
case crypto.SigTypeSecp256k1:
*kt = KTSecp256k1
case crypto.SigTypeDelegated:
*kt = KTDelegated
default:
return fmt.Errorf("unknown sigtype: %d", bst)
}
@ -51,6 +53,7 @@ const (
KTBLS KeyType = "bls"
KTSecp256k1 KeyType = "secp256k1"
KTSecp256k1Ledger KeyType = "secp256k1-ledger"
KTDelegated KeyType = "delegated"
)
// KeyInfo is used for storing keys in KeyStore

View File

@ -19,6 +19,7 @@ import (
"github.com/filecoin-project/lotus/chain/vectors"
"github.com/filecoin-project/lotus/chain/wallet"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
)

View File

@ -111,6 +111,7 @@ var Prices = map[abi.ChainEpoch]Pricelist{
verifySignature: map[crypto.SigType]int64{
crypto.SigTypeBLS: 16598605,
crypto.SigTypeSecp256k1: 1637292,
crypto.SigTypeDelegated: 1637292,
},
hashingBase: 31355,

View File

@ -45,7 +45,7 @@ func NewKey(keyinfo types.KeyInfo) (*Key, error) {
}
switch k.Type {
case types.KTSecp256k1:
case types.KTSecp256k1, types.KTDelegated:
k.Address, err = address.NewSecp256k1Address(k.PublicKey)
if err != nil {
return nil, xerrors.Errorf("converting Secp256k1 to address: %w", err)
@ -68,6 +68,8 @@ func ActSigType(typ types.KeyType) crypto.SigType {
return crypto.SigTypeBLS
case types.KTSecp256k1:
return crypto.SigTypeSecp256k1
case types.KTDelegated:
return crypto.SigTypeDelegated
default:
return crypto.SigTypeUnknown
}

View File

@ -16,7 +16,8 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet/key"
"github.com/filecoin-project/lotus/lib/sigs"
_ "github.com/filecoin-project/lotus/lib/sigs/bls" // enable bls signatures
_ "github.com/filecoin-project/lotus/lib/sigs/bls" // enable bls signatures
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp" // enable secp signatures
)

View File

@ -41,6 +41,7 @@ import (
"github.com/filecoin-project/lotus/chain/vm"
lcli "github.com/filecoin-project/lotus/cli"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/filecoin-project/lotus/node/repo"
"github.com/filecoin-project/lotus/storage/sealer/ffiwrapper"

View File

@ -10,6 +10,7 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/wallet"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
)

View File

@ -23,6 +23,7 @@ import (
"github.com/filecoin-project/lotus/chain/wallet"
"github.com/filecoin-project/lotus/chain/wallet/key"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/filecoin-project/lotus/node/modules"
"github.com/filecoin-project/lotus/node/modules/lp2p"

View File

@ -27,7 +27,8 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/chain/vm"
"github.com/filecoin-project/lotus/conformance/chaos"
_ "github.com/filecoin-project/lotus/lib/sigs/bls" // enable bls signatures
_ "github.com/filecoin-project/lotus/lib/sigs/bls" // enable bls signatures
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp" // enable secp signatures
"github.com/filecoin-project/lotus/storage/sealer/ffiwrapper"
)

View File

@ -2186,7 +2186,7 @@ Inputs:
[
{
"from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707",
"to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
"gas": "0x5",
"gasPrice": "0x0",
"value": "0x0",
@ -2217,13 +2217,12 @@ Inputs:
[
{
"from": "0x0707070707070707070707070707070707070707",
"to": "0x0707070707070707070707070707070707070707",
"to": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
"gas": "0x5",
"gasPrice": "0x0",
"value": "0x0",
"data": "0x07"
},
"string value"
}
]
```
@ -2302,7 +2301,7 @@ Perms: read
Inputs:
```json
[
"0x5",
"string value",
true
]
```
@ -2412,7 +2411,7 @@ Response:
```json
{
"chainId": "0x5",
"nonce": 42,
"nonce": "0x5",
"hash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x5",
@ -2423,11 +2422,12 @@ Response:
"type": "0x5",
"input": "0x07",
"gas": "0x5",
"gasLimit": "0x5",
"maxFeePerGas": "0x0",
"maxPriorityFeePerGas": "0x0",
"v": "0x0",
"r": "0x0",
"s": "0x0"
"v": "0x07",
"r": "0x07",
"s": "0x07"
}
```
@ -2448,7 +2448,7 @@ Response:
```json
{
"chainId": "0x5",
"nonce": 42,
"nonce": "0x5",
"hash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x5",
@ -2459,11 +2459,12 @@ Response:
"type": "0x5",
"input": "0x07",
"gas": "0x5",
"gasLimit": "0x5",
"maxFeePerGas": "0x0",
"maxPriorityFeePerGas": "0x0",
"v": "0x0",
"r": "0x0",
"s": "0x0"
"v": "0x07",
"r": "0x07",
"s": "0x07"
}
```
@ -2475,7 +2476,7 @@ Perms: read
Inputs:
```json
[
"0x0707070707070707070707070707070707070707070707070707070707070707"
"0x37690cfec6c1bf4c3b9288c7a5d783e98731e90b0a4c177c2a374c7a9427355e"
]
```
@ -2483,7 +2484,7 @@ Response:
```json
{
"chainId": "0x5",
"nonce": 42,
"nonce": "0x5",
"hash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x5",
@ -2494,11 +2495,12 @@ Response:
"type": "0x5",
"input": "0x07",
"gas": "0x5",
"gasLimit": "0x5",
"maxFeePerGas": "0x0",
"maxPriorityFeePerGas": "0x0",
"v": "0x0",
"r": "0x0",
"s": "0x0"
"v": "0x07",
"r": "0x07",
"s": "0x07"
}
```
@ -2533,7 +2535,7 @@ Response:
```json
{
"transactionHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"transacionIndex": "0x5",
"transactionIndex": "0x5",
"blockHash": "0x0707070707070707070707070707070707070707070707070707070707070707",
"blockNumber": "0x5",
"from": "0x0707070707070707070707070707070707070707",
@ -2543,7 +2545,11 @@ Response:
"contractAddress": "0x5cbeecf99d3fdb3f25e309cc264f240bb0664031",
"cumulativeGasUsed": "0x5",
"gasUsed": "0x5",
"effectiveGasPrice": "0x0"
"effectiveGasPrice": "0x0",
"logsBloom": "0x07",
"logs": [
"string value"
]
}
```

View File

@ -25,6 +25,7 @@ import (
"github.com/filecoin-project/lotus/chain/types"
"github.com/filecoin-project/lotus/lib/sigs"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/filecoin-project/lotus/metrics"
"github.com/filecoin-project/lotus/node/impl/full"

2
go.sum
View File

@ -345,6 +345,8 @@ github.com/filecoin-project/go-state-types v0.1.8/go.mod h1:UwGVoMsULoCK+bWjEdd/
github.com/filecoin-project/go-state-types v0.1.10/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q=
github.com/filecoin-project/go-state-types v0.9.10-0.20221109071515-5536f160fe27 h1:1/o3al2zThas9rW6xqPKAzjs4q2tiBHaBNBP2sx0Vc4=
github.com/filecoin-project/go-state-types v0.9.10-0.20221109071515-5536f160fe27/go.mod h1:7ty480tvttEAqWKywhAaDCElk7ksTqEXtXWAzTSdEKo=
github.com/filecoin-project/go-state-types v0.1.11-0.20220923222823-af1291888ee8 h1:iu/6jTVXyZDN/RBh2M2+K0i1ML+4DxydDJ/Bm7BjhjA=
github.com/filecoin-project/go-state-types v0.1.11-0.20220923222823-af1291888ee8/go.mod h1:UwGVoMsULoCK+bWjEdd/xLCvLAQFBC7EDT477SKml+Q=
github.com/filecoin-project/go-statemachine v0.0.0-20200925024713-05bd7c71fbfe/go.mod h1:FGwQgZAt2Gh5mjlwJUlVB62JeYdo+if0xWxSEfBD9ig=
github.com/filecoin-project/go-statemachine v1.0.2 h1:421SSWBk8GIoCoWYYTE/d+qCWccgmRH0uXotXRDjUbc=
github.com/filecoin-project/go-statemachine v1.0.2/go.mod h1:jZdXXiHa61n4NmgWFG4w8tnqgvZVHYbJ3yW7+y8bF54=

View File

@ -0,0 +1,57 @@
package delegated
import (
"fmt"
"golang.org/x/crypto/sha3"
"github.com/filecoin-project/go-address"
gocrypto "github.com/filecoin-project/go-crypto"
crypto1 "github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/lib/sigs"
)
type delegatedSigner struct{}
func (delegatedSigner) GenPrivate() ([]byte, error) {
priv, err := gocrypto.GenerateKey()
if err != nil {
return nil, err
}
return priv, nil
}
func (delegatedSigner) ToPublic(pk []byte) ([]byte, error) {
return gocrypto.PublicKey(pk), nil
}
func (delegatedSigner) Sign(pk []byte, msg []byte) ([]byte, error) {
return nil, fmt.Errorf("not implemented")
}
func (delegatedSigner) Verify(sig []byte, a address.Address, msg []byte) error {
hasher := sha3.NewLegacyKeccak256()
hasher.Write(msg)
hash := hasher.Sum(nil)
pubk, err := gocrypto.EcRecover(hash, sig)
if err != nil {
return err
}
maybeaddr, err := address.NewSecp256k1Address(pubk)
if err != nil {
return err
}
if maybeaddr != a {
return fmt.Errorf("signature did not match")
}
return nil
}
func init() {
sigs.RegisterSignature(crypto1.SigTypeDelegated, delegatedSigner{})
}

View File

@ -6,7 +6,7 @@ import (
"github.com/minio/blake2b-simd"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-crypto"
gocrypto "github.com/filecoin-project/go-crypto"
crypto2 "github.com/filecoin-project/go-state-types/crypto"
"github.com/filecoin-project/lotus/lib/sigs"
@ -15,7 +15,7 @@ import (
type secpSigner struct{}
func (secpSigner) GenPrivate() ([]byte, error) {
priv, err := crypto.GenerateKey()
priv, err := gocrypto.GenerateKey()
if err != nil {
return nil, err
}
@ -23,12 +23,12 @@ func (secpSigner) GenPrivate() ([]byte, error) {
}
func (secpSigner) ToPublic(pk []byte) ([]byte, error) {
return crypto.PublicKey(pk), nil
return gocrypto.PublicKey(pk), nil
}
func (secpSigner) Sign(pk []byte, msg []byte) ([]byte, error) {
b2sum := blake2b.Sum256(msg)
sig, err := crypto.Sign(pk, b2sum[:])
sig, err := gocrypto.Sign(pk, b2sum[:])
if err != nil {
return nil, err
}
@ -38,7 +38,7 @@ func (secpSigner) Sign(pk []byte, msg []byte) ([]byte, error) {
func (secpSigner) Verify(sig []byte, a address.Address, msg []byte) error {
b2sum := blake2b.Sum256(msg)
pubk, err := crypto.EcRecover(b2sum[:], sig)
pubk, err := gocrypto.EcRecover(b2sum[:], sig)
if err != nil {
return err
}

View File

@ -30,6 +30,7 @@ import (
"github.com/filecoin-project/lotus/lib/lotuslog"
"github.com/filecoin-project/lotus/lib/peermgr"
_ "github.com/filecoin-project/lotus/lib/sigs/bls"
_ "github.com/filecoin-project/lotus/lib/sigs/delegated"
_ "github.com/filecoin-project/lotus/lib/sigs/secp"
"github.com/filecoin-project/lotus/markets/storageadapter"
"github.com/filecoin-project/lotus/node/config"

View File

@ -2,18 +2,25 @@ package full
import (
"context"
"fmt"
"strconv"
"go.uber.org/fx"
"golang.org/x/xerrors"
"github.com/filecoin-project/go-address"
"github.com/filecoin-project/go-state-types/abi"
"github.com/filecoin-project/go-state-types/big"
builtintypes "github.com/filecoin-project/go-state-types/builtin"
"github.com/filecoin-project/go-state-types/builtin/v8/evm"
init8 "github.com/filecoin-project/go-state-types/builtin/v8/init"
"github.com/filecoin-project/go-state-types/exitcode"
"github.com/filecoin-project/specs-actors/actors/builtin"
"github.com/filecoin-project/lotus/api"
"github.com/filecoin-project/lotus/build"
"github.com/filecoin-project/lotus/chain/actors"
builtinactors "github.com/filecoin-project/lotus/chain/actors/builtin"
"github.com/filecoin-project/lotus/chain/messagepool"
"github.com/filecoin-project/lotus/chain/stmgr"
"github.com/filecoin-project/lotus/chain/store"
@ -27,10 +34,10 @@ type EthModuleAPI interface {
EthGetBlockTransactionCountByNumber(ctx context.Context, blkNum api.EthInt) (api.EthInt, error)
EthGetBlockTransactionCountByHash(ctx context.Context, blkHash api.EthHash) (api.EthInt, error)
EthGetBlockByHash(ctx context.Context, blkHash api.EthHash, fullTxInfo bool) (api.EthBlock, error)
EthGetBlockByNumber(ctx context.Context, blkNum api.EthInt, fullTxInfo bool) (api.EthBlock, error)
EthGetTransactionByHash(ctx context.Context, txHash api.EthHash) (api.EthTx, error)
EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (api.EthBlock, error)
EthGetTransactionByHash(ctx context.Context, txHash *api.EthHash) (*api.EthTx, error)
EthGetTransactionCount(ctx context.Context, sender api.EthAddress, blkOpt string) (api.EthInt, error)
EthGetTransactionReceipt(ctx context.Context, txHash api.EthHash) (api.EthTxReceipt, error)
EthGetTransactionReceipt(ctx context.Context, txHash api.EthHash) (*api.EthTxReceipt, error)
EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash api.EthHash, txIndex api.EthInt) (api.EthTx, error)
EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum api.EthInt, txIndex api.EthInt) (api.EthTx, error)
EthGetCode(ctx context.Context, address api.EthAddress) (string, error)
@ -41,7 +48,7 @@ type EthModuleAPI interface {
NetListening(ctx context.Context) (bool, error)
EthProtocolVersion(ctx context.Context) (api.EthInt, error)
EthGasPrice(ctx context.Context) (api.EthBigInt, error)
EthEstimateGas(ctx context.Context, tx api.EthCall, blkParam string) (api.EthInt, error)
EthEstimateGas(ctx context.Context, tx api.EthCall) (api.EthInt, error)
EthCall(ctx context.Context, tx api.EthCall, blkParam string) (api.EthBytes, error)
EthMaxPriorityFeePerGas(ctx context.Context) (api.EthBigInt, error)
EthSendRawTransaction(ctx context.Context, rawTx api.EthBytes) (api.EthHash, error)
@ -129,27 +136,38 @@ func (a *EthModule) EthGetBlockByHash(ctx context.Context, blkHash api.EthHash,
return a.ethBlockFromFilecoinTipSet(ctx, ts, fullTxInfo)
}
func (a *EthModule) EthGetBlockByNumber(ctx context.Context, blkNum api.EthInt, fullTxInfo bool) (api.EthBlock, error) {
ts, err := a.Chain.GetTipsetByHeight(ctx, abi.ChainEpoch(blkNum), nil, false)
func (a *EthModule) EthGetBlockByNumber(ctx context.Context, blkNum string, fullTxInfo bool) (api.EthBlock, error) {
var num api.EthInt
err := num.UnmarshalJSON([]byte(`"` + blkNum + `"`))
if err != nil {
num = api.EthInt(a.Chain.GetHeaviestTipSet().Height())
}
ts, err := a.Chain.GetTipsetByHeight(ctx, abi.ChainEpoch(num), nil, false)
if err != nil {
return api.EthBlock{}, xerrors.Errorf("error loading tipset %s: %w", ts, err)
}
return a.ethBlockFromFilecoinTipSet(ctx, ts, fullTxInfo)
}
func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash api.EthHash) (api.EthTx, error) {
func (a *EthModule) EthGetTransactionByHash(ctx context.Context, txHash *api.EthHash) (*api.EthTx, error) {
// Ethereum's behavior is to return null when the txHash is invalid, so we use nil to check if txHash is valid
if txHash == nil {
return nil, nil
}
cid := txHash.ToCid()
msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, cid, api.LookbackNoLimit, true)
if err != nil {
return api.EthTx{}, nil
return nil, nil
}
tx, err := a.ethTxFromFilecoinMessageLookup(ctx, msgLookup)
if err != nil {
return api.EthTx{}, err
return nil, nil
}
return tx, nil
return &tx, nil
}
func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender api.EthAddress, blkParam string) (api.EthInt, error) {
@ -164,29 +182,29 @@ func (a *EthModule) EthGetTransactionCount(ctx context.Context, sender api.EthAd
return api.EthInt(nonce), nil
}
func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash api.EthHash) (api.EthTxReceipt, error) {
func (a *EthModule) EthGetTransactionReceipt(ctx context.Context, txHash api.EthHash) (*api.EthTxReceipt, error) {
cid := txHash.ToCid()
msgLookup, err := a.StateAPI.StateSearchMsg(ctx, types.EmptyTSK, cid, api.LookbackNoLimit, true)
if err != nil {
return api.EthTxReceipt{}, err
return nil, err
}
tx, err := a.ethTxFromFilecoinMessageLookup(ctx, msgLookup)
if err != nil {
return api.EthTxReceipt{}, err
return nil, err
}
replay, err := a.StateAPI.StateReplay(ctx, types.EmptyTSK, cid)
if err != nil {
return api.EthTxReceipt{}, err
return nil, err
}
receipt, err := api.NewEthTxReceipt(tx, msgLookup, replay)
if err != nil {
return api.EthTxReceipt{}, err
return nil, err
}
return receipt, nil
return &receipt, nil
}
func (a *EthModule) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash api.EthHash, txIndex api.EthInt) (api.EthTx, error) {
@ -267,24 +285,93 @@ func (a *EthModule) EthGasPrice(ctx context.Context) (api.EthBigInt, error) {
}
func (a *EthModule) EthSendRawTransaction(ctx context.Context, rawTx api.EthBytes) (api.EthHash, error) {
return api.EthHash{}, nil
txArgs, err := api.ParseEthTxArgs(rawTx)
if err != nil {
return api.EmptyEthHash, err
}
smsg, err := txArgs.ToSignedMessage()
if err != nil {
return api.EmptyEthHash, err
}
cid, err := a.MpoolAPI.MpoolPush(ctx, smsg)
if err != nil {
return api.EmptyEthHash, err
}
return api.EthHashFromCid(cid)
}
func (a *EthModule) applyEvmMsg(ctx context.Context, tx api.EthCall) (*api.InvocResult, error) {
from, err := tx.From.ToFilecoinAddress()
if err != nil {
return nil, err
// FIXME: this is a workaround, remove this when f4 address is ready
var from address.Address
var err error
if tx.From[0] == 0xff && tx.From[1] == 0 && tx.From[2] == 0 {
addr, err := tx.From.ToFilecoinAddress()
if err != nil {
return nil, err
}
from = addr
} else {
id := uint64(100)
for ; id < 300; id++ {
idAddr, err := address.NewIDAddress(id)
if err != nil {
return nil, err
}
from = idAddr
act, err := a.StateGetActor(ctx, idAddr, types.EmptyTSK)
if err != nil {
return nil, err
}
if builtinactors.IsAccountActor(act.Code) {
break
}
}
if id == 300 {
return nil, fmt.Errorf("cannot find a dummy account")
}
}
to, err := tx.To.ToFilecoinAddress()
if err != nil {
return nil, xerrors.Errorf("cannot get Filecoin address: %w", err)
var params []byte
var to address.Address
if tx.To == nil {
to = builtintypes.InitActorAddr
constructorParams, err := actors.SerializeParams(&evm.ConstructorParams{
Bytecode: tx.Data,
InputData: []byte{},
})
if err != nil {
return nil, fmt.Errorf("failed to serialize constructor params: %w", err)
}
evmActorCid, ok := actors.GetActorCodeID(actors.Version8, "evm")
if !ok {
return nil, fmt.Errorf("failed to lookup evm actor code CID")
}
params, err = actors.SerializeParams(&init8.ExecParams{
CodeCID: evmActorCid,
ConstructorParams: constructorParams,
})
if err != nil {
return nil, fmt.Errorf("failed to serialize init actor exec params: %w", err)
}
} else {
addr, err := tx.To.ToFilecoinAddress()
if err != nil {
return nil, xerrors.Errorf("cannot get Filecoin address: %w", err)
}
to = addr
params = tx.Data
}
msg := &types.Message{
From: from,
To: to,
Value: big.Int(tx.Value),
Method: abi.MethodNum(2),
Params: tx.Data,
Params: params,
GasLimit: build.BlockGasLimit,
GasFeeCap: big.Zero(),
GasPremium: big.Zero(),
@ -312,7 +399,7 @@ func (a *EthModule) applyEvmMsg(ctx context.Context, tx api.EthCall) (*api.Invoc
return res, nil
}
func (a *EthModule) EthEstimateGas(ctx context.Context, tx api.EthCall, blkParam string) (api.EthInt, error) {
func (a *EthModule) EthEstimateGas(ctx context.Context, tx api.EthCall) (api.EthInt, error) {
invokeResult, err := a.applyEvmMsg(ctx, tx)
if err != nil {
return api.EthInt(0), err
@ -388,6 +475,9 @@ func (a *EthModule) ethBlockFromFilecoinTipSet(ctx context.Context, ts *types.Ti
}
func (a *EthModule) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLookup *api.MsgLookup) (api.EthTx, error) {
if msgLookup == nil {
return api.EthTx{}, fmt.Errorf("msg does not exist")
}
cid := msgLookup.Message
txHash, err := api.EthHashFromCid(cid)
if err != nil {
@ -447,9 +537,9 @@ func (a *EthModule) ethTxFromFilecoinMessageLookup(ctx context.Context, msgLooku
Gas: api.EthInt(msg.GasLimit),
MaxFeePerGas: api.EthBigInt(msg.GasFeeCap),
MaxPriorityFeePerGas: api.EthBigInt(msg.GasPremium),
V: api.EthBigIntZero,
R: api.EthBigIntZero,
S: api.EthBigIntZero,
V: api.EthBytes{},
R: api.EthBytes{},
S: api.EthBytes{},
Input: msg.Params,
}
return tx, nil