/
utils.go
141 lines (127 loc) · 3.22 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package precompiles
import (
"context"
"errors"
"fmt"
"github.com/cosmos/cosmos-sdk/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/gogoproto/proto"
"github.com/ethereum/go-ethereum/common"
"github.com/evmos/ethermint/x/evm/statedb"
)
type NativeMessage interface {
proto.Message
GetSigners() []sdk.AccAddress
}
type Executor struct {
cdc codec.Codec
stateDB ExtStateDB
caller common.Address
contract common.Address
input []byte
input2 []byte
converter statedb.EventConverter
}
// exec is a generic function that executes the given action in statedb, and marshal/unmarshal the input/output
func exec[Req any, PReq interface {
*Req
NativeMessage
}, Resp proto.Message](
e *Executor,
action func(context.Context, PReq) (Resp, error),
) ([]byte, error) {
msg := PReq(new(Req))
if err := e.cdc.Unmarshal(e.input, msg); err != nil {
return nil, fmt.Errorf("fail to Unmarshal %T %w", msg, err)
}
signers := msg.GetSigners()
if len(signers) != 1 {
return nil, errors.New("don't support multi-signers message")
}
caller := common.BytesToAddress(signers[0].Bytes())
if caller != e.caller {
return nil, fmt.Errorf("caller is not authenticated: expected %s, got %s", e.caller.Hex(), caller.Hex())
}
var res Resp
if err := e.stateDB.ExecuteNativeAction(e.contract, e.converter, func(ctx sdk.Context) error {
var err error
res, err = action(ctx, msg)
return err
}); err != nil {
return nil, err
}
output, err := e.cdc.Marshal(res)
if err != nil {
return nil, fmt.Errorf("fail to Marshal %T %w", res, err)
}
return output, nil
}
func execMultipleWithHooks[Req any,
PReq interface {
*Req
NativeMessage
},
Resp proto.Message,
Req2 any,
PReq2 interface {
*Req2
NativeMessage
},
Resp2 proto.Message,
](
e *Executor,
preAction func(sdk.Context, PReq, PReq2) error,
action func(context.Context, PReq) (Resp, error),
action2 func(context.Context, PReq2) (Resp2, error),
) ([]byte, error) {
msg := PReq(new(Req))
if err := e.cdc.Unmarshal(e.input, msg); err != nil {
return nil, fmt.Errorf("fail to Unmarshal %T %w", msg, err)
}
msg2 := PReq2(new(Req2))
if err := e.cdc.Unmarshal(e.input2, msg2); err != nil {
return nil, fmt.Errorf("fail to Unmarshal %T %w", msg2, err)
}
signers := msg.GetSigners()
if len(signers) != 1 {
return nil, fmt.Errorf("expected 1 signer, got %d", len(signers))
}
if common.BytesToAddress(signers[0].Bytes()) != e.caller {
return nil, errors.New("caller is not authenticated")
}
var res Resp
if err := e.stateDB.ExecuteNativeAction(e.contract, e.converter, func(ctx sdk.Context) (err error) {
if preAction != nil {
if err = preAction(ctx, msg, msg2); err != nil {
return err
}
}
res, err = action(ctx, msg)
if err == nil && len(e.input2) > 0 {
_, err = action2(ctx, msg2)
}
return
}); err != nil {
return nil, err
}
return e.cdc.Marshal(res)
}
func execMultiple[Req any,
PReq interface {
*Req
NativeMessage
},
Resp proto.Message,
Req2 any,
PReq2 interface {
*Req2
NativeMessage
},
Resp2 proto.Message,
](
e *Executor,
action func(context.Context, PReq) (Resp, error),
action2 func(context.Context, PReq2) (Resp2, error),
) ([]byte, error) {
return execMultipleWithHooks(e, nil, action, action2)
}