-
Notifications
You must be signed in to change notification settings - Fork 101
/
ante.go
235 lines (219 loc) · 7.98 KB
/
ante.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
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
package auth
import (
"bytes"
"encoding/hex"
"fmt"
"os"
"github.com/pokt-network/pocket-core/codec"
posCrypto "github.com/pokt-network/pocket-core/crypto"
sdk "github.com/pokt-network/pocket-core/types"
"github.com/pokt-network/pocket-core/x/auth/keeper"
"github.com/pokt-network/pocket-core/x/auth/types"
"github.com/tendermint/tendermint/state/txindex"
tmTypes "github.com/tendermint/tendermint/types"
)
// NewAnteHandler returns an AnteHandler that checks signatures and deducts fees from the first signer.
func NewAnteHandler(ak keeper.Keeper) sdk.AnteHandler {
return func(ctx sdk.Ctx, tx sdk.Tx, txBz []byte, txIndexer txindex.TxIndexer, simulate bool) (newCtx sdk.Ctx, res sdk.Result, signer posCrypto.PublicKey, abort bool) {
if addr := ak.GetModuleAddress(types.FeeCollectorName); addr == nil {
ctx.Logger().Error(fmt.Sprintf("%s module account has not been set", types.FeeCollectorName))
os.Exit(1)
}
// validate the transaction
if err := tx.ValidateBasic(); err != nil {
return newCtx, err.Result(), nil, true
}
stdTx, ok := tx.(types.StdTx)
if !ok {
return newCtx, sdk.ErrInternal("all transactions must be convertible to inteface: ProtoStdTx").Result(), nil, true
}
signer, err := ValidateTransaction(ctx, ak, stdTx, ak.GetParams(ctx), txIndexer, txBz, simulate)
if err != nil {
return newCtx, err.Result(), signer, true
}
err = DeductFees(ak, ctx, stdTx, signer)
if err != nil {
return newCtx, err.Result(), signer, true
}
return ctx, sdk.Result{}, signer, false // continue...
}
}
func ValidateTransaction(ctx sdk.Ctx, k Keeper, stdTx types.StdTx, params Params, txIndexer txindex.TxIndexer, txBz []byte, simulate bool) (signer posCrypto.PublicKey, sdkErr sdk.Error) {
// validate the memo
if err := ValidateMemo(stdTx, params); err != nil {
return nil, types.ErrInvalidMemo(ModuleName, err)
}
// check for duplicate transaction to prevent replay attacks
txHash := tmTypes.Tx(txBz).Hash()
// make http call to tendermint to check txIndexer
if txIndexer == nil {
ctx.Logger().Error(types.ErrNilTxIndexer(ModuleName).Error())
return nil, types.ErrNilTxIndexer(ModuleName)
}
res, err := (txIndexer).Get(txHash)
if err != nil {
ctx.Logger().Error(err.Error())
return nil, sdk.ErrInternal(err.Error())
}
if res != nil {
return nil, types.ErrDuplicateTx(ModuleName, hex.EncodeToString(txHash))
}
// Please note that GetSigners() is simply redirected to Msg.GetSigners()
// and does not return the actual signer of this transaction in order to
// prevent transactions from being accepted unconditionally.
// If you want to allow a transaction signed by an address that is not
// included in this return value, add a specific condition case by case.
validSigners := stdTx.GetSigners()
if k.Cdc.IsAfterNonCustodialUpgrade(ctx.BlockHeight()) &&
k.Cdc.IsAfterOutputAddressEditorUpgrade(ctx.BlockHeight()) {
// MsgStake may be signed by the current output address. We need to ask
// the node keeper to retrieve the current output address.
validSigners = append(validSigners,
k.POSKeeper.GetMsgStakeOutputSigner(ctx, stdTx.Msg))
}
if ctx.IsAfterUpgradeHeight() &&
k.Cdc.IsAfterAppTransferUpgrade(ctx.BlockHeight()) {
msgSigner := sdk.Address(stdTx.Signature.Address())
// AppMsgStake has a special case of AppTransfer. In that case, we accept
// a message where the pubkey is different from the signer, delegating
// most of validation work to the app's message handler after fee deduction.
if k.AppKeeper.IsMsgAppTransfer(ctx, msgSigner, stdTx.Msg) {
validSigners = append(validSigners, msgSigner)
}
}
var pk posCrypto.PublicKey
for _, signer := range validSigners {
// attempt to get the public key from the signature
if stdTx.GetSignature().GetPublicKey() != "" {
var err error
pk, err = posCrypto.NewPublicKey(stdTx.GetSignature().GetPublicKey())
if err != nil {
return nil, sdk.ErrInvalidPubKey(err.Error())
}
} else {
// public key in the signature not found so check world state
acc := k.GetAccount(ctx, signer)
if acc == nil {
return nil, types.ErrAccountNotFound(ModuleName)
}
if pk = acc.GetPubKey(); pk == nil {
return nil, types.ErrEmptyPublicKey(ModuleName)
}
}
//patch sync fix : add Verify against after codec upgrade chainhalt height
if !bytes.Equal(pk.Address(), signer) && ctx.BlockHeight() != codec.CodecChainHaltHeight {
continue
}
// get the sign bytes from the tx
signBytes, err := GetSignBytes(ctx.ChainID(), stdTx)
if err != nil {
return nil, sdk.ErrInternal(err.Error())
}
// get the fees from the tx
expectedFee := sdk.NewCoins(sdk.NewCoin(sdk.DefaultStakeDenom, k.GetParams(ctx).FeeMultiplier.GetFee(stdTx.GetMsg())))
// test for public key type
p, ok := pk.(posCrypto.PublicKeyMultiSig)
// if standard public key
if !ok {
// validate the fees for a standard public key
if !stdTx.GetFee().IsAllGTE(expectedFee) {
return nil, types.ErrInsufficientFee(ModuleName, expectedFee, stdTx.GetFee())
}
// validate signature for regular public key
if !simulate && !pk.VerifyBytes(signBytes, stdTx.GetSignature().GetSignature()) {
continue
}
return pk, nil
}
// validate the signature depth
ok = ValidateSignatureDepth(params.TxSigLimit, p)
if !ok {
return nil, types.ErrTooManySignatures(ModuleName, params.TxSigLimit)
}
// validate the multi sig
if !simulate && !pk.VerifyBytes(signBytes, stdTx.GetSignature().GetSignature()) {
continue
}
return pk, nil
}
return nil, sdk.ErrUnauthorized("signature verification failed for the transaction")
}
func ValidateSignatureDepth(limit uint64, publicKey posCrypto.PublicKeyMultiSig) (ok bool) {
_, ok = recSignDepth(1, limit, publicKey)
return
}
// recSignDepth ensures that the number of signatures does not exceed the max
func recSignDepth(count, limit uint64, publicKey posCrypto.PublicKeyMultiSig) (c uint64, ok bool) {
for _, p := range publicKey.Keys() {
count++
if pk, ok := p.(posCrypto.PublicKeyMultiSig); ok {
count, ok = recSignDepth(count, limit, pk)
if !ok {
return count, ok
}
}
if count > limit {
return count, false
}
}
return count, true
}
// GetSignerAcc returns an account for a given address that is expected to sign
// a transaction.
func GetSignerAcc(ctx sdk.Ctx, ak keeper.Keeper, addr sdk.Address) (Account, sdk.Error) {
if acc := ak.GetAccount(ctx, addr); acc != nil {
return acc, nil
}
return nil, sdk.ErrUnknownAddress(fmt.Sprintf("account %s does not exist", addr))
}
// ValidateMemo validates the memo size.
func ValidateMemo(stdTx types.StdTx, params Params) sdk.Error {
memoLength := len(stdTx.GetMemo())
if uint64(memoLength) > params.MaxMemoCharacters {
return sdk.ErrMemoTooLarge(
fmt.Sprintf(
"maximum number of characters is %d but received %d characters",
params.MaxMemoCharacters, memoLength,
),
)
}
return nil
}
// DeductFees deducts fees from the given account.
func DeductFees(keeper keeper.Keeper, ctx sdk.Ctx, tx types.StdTx, signer posCrypto.PublicKey) sdk.Error {
fees := tx.GetFee()
if !fees.IsValid() {
return sdk.ErrInsufficientFee(fmt.Sprintf("invalid fee amount: %s", fees))
}
var acc Account
var err sdk.Error
if keeper.Cdc.IsAfterNonCustodialUpgrade(ctx.BlockHeight()) {
acc, err = GetSignerAcc(ctx, keeper, sdk.Address(signer.Address()))
if err != nil {
return err
}
} else {
acc, err = GetSignerAcc(ctx, keeper, tx.GetSigners()[0])
if err != nil {
return err
}
}
coins := acc.GetCoins()
// verify the account has enough funds to pay for fees
_, hasNeg := coins.SafeSub(fees)
if hasNeg {
return types.ErrInsufficientBalance(ModuleName, acc.GetAddress(), fees)
}
err = keeper.SendCoinsFromAccountToModule(ctx, acc.GetAddress(), types.FeeCollectorName, fees)
if err != nil {
return err
}
return nil
}
// GetSignBytes returns a slice of bytes to sign over for a given transaction
// and an account.
func GetSignBytes(chainID string, stdTx types.StdTx) ([]byte, error) {
return StdSignBytes(
chainID, stdTx.GetEntropy(), stdTx.GetFee(), stdTx.GetMsg(), stdTx.GetMemo(),
)
}