-
Notifications
You must be signed in to change notification settings - Fork 0
/
config.go
212 lines (188 loc) · 7.83 KB
/
config.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
// (c) 2023, Lux Partners Limited. All rights reserved.
// See the file LICENSE for licensing terms.
package warp
import (
"context"
"errors"
"fmt"
"github.com/luxdefi/node/vms/platformvm/warp"
"github.com/luxdefi/node/vms/platformvm/warp/payload"
"github.com/luxdefi/subnet-evm/params"
"github.com/luxdefi/subnet-evm/precompile/precompileconfig"
"github.com/luxdefi/subnet-evm/predicate"
warpValidators "github.com/luxdefi/subnet-evm/warp/validators"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/log"
)
var (
_ precompileconfig.Config = &Config{}
_ precompileconfig.Predicater = &Config{}
_ precompileconfig.Accepter = &Config{}
)
var (
errOverflowSignersGasCost = errors.New("overflow calculating warp signers gas cost")
errInvalidPredicateBytes = errors.New("cannot unpack predicate bytes")
errInvalidWarpMsg = errors.New("cannot unpack warp message")
errCannotParseWarpMsg = errors.New("cannot parse warp message")
errInvalidWarpMsgPayload = errors.New("cannot unpack warp message payload")
errInvalidAddressedPayload = errors.New("cannot unpack addressed payload")
errInvalidBlockHashPayload = errors.New("cannot unpack block hash payload")
errCannotGetNumSigners = errors.New("cannot fetch num signers from warp message")
errWarpCannotBeActivated = errors.New("warp cannot be activated before DUpgrade")
errFailedVerification = errors.New("cannot verify warp signature")
)
// Config implements the precompileconfig.Config interface and
// adds specific configuration for Warp.
type Config struct {
precompileconfig.Upgrade
QuorumNumerator uint64 `json:"quorumNumerator"`
}
// NewConfig returns a config for a network upgrade at [blockTimestamp] that enables
// Warp with the given quorum numerator.
func NewConfig(blockTimestamp *uint64, quorumNumerator uint64) *Config {
return &Config{
Upgrade: precompileconfig.Upgrade{BlockTimestamp: blockTimestamp},
QuorumNumerator: quorumNumerator,
}
}
// NewDefaultConfig returns a config for a network upgrade at [blockTimestamp] that enables
// Warp with the default quorum numerator (0 denotes using the default).
func NewDefaultConfig(blockTimestamp *uint64) *Config {
return NewConfig(blockTimestamp, 0)
}
// NewDisableConfig returns config for a network upgrade at [blockTimestamp]
// that disables Warp.
func NewDisableConfig(blockTimestamp *uint64) *Config {
return &Config{
Upgrade: precompileconfig.Upgrade{
BlockTimestamp: blockTimestamp,
Disable: true,
},
}
}
// Key returns the key for the Warp precompileconfig.
// This should be the same key as used in the precompile module.
func (*Config) Key() string { return ConfigKey }
// Verify tries to verify Config and returns an error accordingly.
func (c *Config) Verify(chainConfig precompileconfig.ChainConfig) error {
if c.Timestamp() != nil {
// If Warp attempts to activate before the DUpgrade, fail verification
timestamp := *c.Timestamp()
if !chainConfig.IsDUpgrade(timestamp) {
return errWarpCannotBeActivated
}
}
if c.QuorumNumerator > params.WarpQuorumDenominator {
return fmt.Errorf("cannot specify quorum numerator (%d) > quorum denominator (%d)", c.QuorumNumerator, params.WarpQuorumDenominator)
}
// If a non-default quorum numerator is specified and it is less than the minimum, return an error
if c.QuorumNumerator != 0 && c.QuorumNumerator < params.WarpQuorumNumeratorMinimum {
return fmt.Errorf("cannot specify quorum numerator (%d) < min quorum numerator (%d)", c.QuorumNumerator, params.WarpQuorumNumeratorMinimum)
}
return nil
}
// Equal returns true if [s] is a [*Config] and it has been configured identical to [c].
func (c *Config) Equal(s precompileconfig.Config) bool {
// typecast before comparison
other, ok := (s).(*Config)
if !ok {
return false
}
equals := c.Upgrade.Equal(&other.Upgrade)
return equals && c.QuorumNumerator == other.QuorumNumerator
}
func (c *Config) Accept(acceptCtx *precompileconfig.AcceptContext, blockHash common.Hash, blockNumber uint64, txHash common.Hash, logIndex int, topics []common.Hash, logData []byte) error {
unsignedMessage, err := UnpackSendWarpEventDataToMessage(logData)
if err != nil {
return fmt.Errorf("failed to parse warp log data into unsigned message (TxHash: %s, LogIndex: %d): %w", txHash, logIndex, err)
}
log.Info(
"Accepted warp unsigned message",
"blockHash", blockHash,
"blockNumber", blockNumber,
"txHash", txHash,
"logIndex", logIndex,
"logData", common.Bytes2Hex(logData),
"warpMessageID", unsignedMessage.ID(),
)
if err := acceptCtx.Warp.AddMessage(unsignedMessage); err != nil {
return fmt.Errorf("failed to add warp message during accept (TxHash: %s, LogIndex: %d): %w", txHash, logIndex, err)
}
return nil
}
// PredicateGas returns the amount of gas necessary to verify the predicate
// PredicateGas charges for:
// 1. Base cost of the message
// 2. Size of the message
// 3. Number of signers
// 4. TODO: Lookup of the validator set
//
// If the payload of the warp message fails parsing, return a non-nil error invalidating the transaction.
func (c *Config) PredicateGas(predicateBytes []byte) (uint64, error) {
totalGas := GasCostPerSignatureVerification
bytesGasCost, overflow := math.SafeMul(GasCostPerWarpMessageBytes, uint64(len(predicateBytes)))
if overflow {
return 0, fmt.Errorf("overflow calculating gas cost for warp message bytes of size %d", len(predicateBytes))
}
totalGas, overflow = math.SafeAdd(totalGas, bytesGasCost)
if overflow {
return 0, fmt.Errorf("overflow adding bytes gas cost of size %d", len(predicateBytes))
}
unpackedPredicateBytes, err := predicate.UnpackPredicate(predicateBytes)
if err != nil {
return 0, fmt.Errorf("%w: %s", errInvalidPredicateBytes, err)
}
warpMessage, err := warp.ParseMessage(unpackedPredicateBytes)
if err != nil {
return 0, fmt.Errorf("%w: %s", errInvalidWarpMsg, err)
}
_, err = payload.Parse(warpMessage.Payload)
if err != nil {
return 0, fmt.Errorf("%w: %s", errInvalidWarpMsgPayload, err)
}
numSigners, err := warpMessage.Signature.NumSigners()
if err != nil {
return 0, fmt.Errorf("%w: %s", errCannotGetNumSigners, err)
}
signerGas, overflow := math.SafeMul(uint64(numSigners), GasCostPerWarpSigner)
if overflow {
return 0, errOverflowSignersGasCost
}
totalGas, overflow = math.SafeAdd(totalGas, signerGas)
if overflow {
return 0, fmt.Errorf("overflow adding signer gas (PrevTotal: %d, VerificationGas: %d)", totalGas, signerGas)
}
return totalGas, nil
}
// VerifyPredicate returns whether the predicate described by [predicateBytes] passes verification.
func (c *Config) VerifyPredicate(predicateContext *precompileconfig.PredicateContext, predicateBytes []byte) error {
unpackedPredicateBytes, err := predicate.UnpackPredicate(predicateBytes)
if err != nil {
return fmt.Errorf("%w: %w", errInvalidPredicateBytes, err)
}
// Note: PredicateGas should be called before VerifyPredicate, so we should never reach an error case here.
warpMsg, err := warp.ParseMessage(unpackedPredicateBytes)
if err != nil {
return fmt.Errorf("%w: %w", errCannotParseWarpMsg, err)
}
quorumNumerator := params.WarpDefaultQuorumNumerator
if c.QuorumNumerator != 0 {
quorumNumerator = c.QuorumNumerator
}
log.Debug("verifying warp message", "warpMsg", warpMsg, "quorumNum", quorumNumerator, "quorumDenom", params.WarpQuorumDenominator)
err = warpMsg.Signature.Verify(
context.Background(),
&warpMsg.UnsignedMessage,
predicateContext.SnowCtx.NetworkID,
warpValidators.NewState(predicateContext.SnowCtx), // Wrap validators.State on the chain snow context to special case the Primary Network
predicateContext.ProposerVMBlockCtx.PChainHeight,
quorumNumerator,
params.WarpQuorumDenominator,
)
if err != nil {
log.Debug("failed to verify warp signature", "msgID", warpMsg.ID(), "err", err)
return fmt.Errorf("%w: %w", errFailedVerification, err)
}
return nil
}