mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-03-01 09:03:48 +00:00
Compare commits
13 commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95665d5703 | ||
|
|
895a8597cb | ||
|
|
46bee92f9e | ||
|
|
abeb78c647 | ||
|
|
ce43eb98de | ||
|
|
638741b082 | ||
|
|
fdfd1235ac | ||
|
|
8ecb68623b | ||
|
|
b9f3a3d964 | ||
|
|
386c3de6c4 | ||
|
|
737ffd1bf0 | ||
|
|
41714b4975 | ||
|
|
d818a9af7b |
10 changed files with 224 additions and 11 deletions
|
|
@ -71,4 +71,7 @@ var (
|
||||||
// ErrInflightTxLimitReached is returned when the maximum number of in-flight
|
// ErrInflightTxLimitReached is returned when the maximum number of in-flight
|
||||||
// transactions is reached for specific accounts.
|
// transactions is reached for specific accounts.
|
||||||
ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts")
|
ErrInflightTxLimitReached = errors.New("in-flight transaction limit reached for delegated accounts")
|
||||||
|
|
||||||
|
// ErrKZGVerificationError is returned when a KZG proof was not verified correctly.
|
||||||
|
ErrKZGVerificationError = errors.New("KZG verification error")
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -193,7 +193,7 @@ func validateBlobSidecarLegacy(sidecar *types.BlobTxSidecar, hashes []common.Has
|
||||||
}
|
}
|
||||||
for i := range sidecar.Blobs {
|
for i := range sidecar.Blobs {
|
||||||
if err := kzg4844.VerifyBlobProof(&sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil {
|
if err := kzg4844.VerifyBlobProof(&sidecar.Blobs[i], sidecar.Commitments[i], sidecar.Proofs[i]); err != nil {
|
||||||
return fmt.Errorf("invalid blob %d: %v", i, err)
|
return fmt.Errorf("%w: invalid blob proof: %v", ErrKZGVerificationError, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
@ -203,7 +203,10 @@ func validateBlobSidecarOsaka(sidecar *types.BlobTxSidecar, hashes []common.Hash
|
||||||
if len(sidecar.Proofs) != len(hashes)*kzg4844.CellProofsPerBlob {
|
if len(sidecar.Proofs) != len(hashes)*kzg4844.CellProofsPerBlob {
|
||||||
return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)*kzg4844.CellProofsPerBlob)
|
return fmt.Errorf("invalid number of %d blob proofs expected %d", len(sidecar.Proofs), len(hashes)*kzg4844.CellProofsPerBlob)
|
||||||
}
|
}
|
||||||
return kzg4844.VerifyCellProofs(sidecar.Blobs, sidecar.Commitments, sidecar.Proofs)
|
if err := kzg4844.VerifyCellProofs(sidecar.Blobs, sidecar.Commitments, sidecar.Proofs); err != nil {
|
||||||
|
return fmt.Errorf("%w: %v", ErrKZGVerificationError, err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidationOptionsWithState define certain differences between stateful transaction
|
// ValidationOptionsWithState define certain differences between stateful transaction
|
||||||
|
|
|
||||||
|
|
@ -124,6 +124,9 @@ func (prv *PrivateKey) GenerateShared(pub *PublicKey, skLen, macLen int) (sk []b
|
||||||
if prv.PublicKey.Curve != pub.Curve {
|
if prv.PublicKey.Curve != pub.Curve {
|
||||||
return nil, ErrInvalidCurve
|
return nil, ErrInvalidCurve
|
||||||
}
|
}
|
||||||
|
if pub.X == nil || pub.Y == nil || !pub.Curve.IsOnCurve(pub.X, pub.Y) {
|
||||||
|
return nil, ErrInvalidPublicKey
|
||||||
|
}
|
||||||
if skLen+macLen > MaxSharedKeyLength(pub) {
|
if skLen+macLen > MaxSharedKeyLength(pub) {
|
||||||
return nil, ErrSharedKeyTooBig
|
return nil, ErrSharedKeyTooBig
|
||||||
}
|
}
|
||||||
|
|
@ -290,7 +293,7 @@ func (prv *PrivateKey) Decrypt(c, s1, s2 []byte) (m []byte, err error) {
|
||||||
switch c[0] {
|
switch c[0] {
|
||||||
case 2, 3, 4:
|
case 2, 3, 4:
|
||||||
rLen = (prv.PublicKey.Curve.Params().BitSize + 7) / 4
|
rLen = (prv.PublicKey.Curve.Params().BitSize + 7) / 4
|
||||||
if len(c) < (rLen + hLen + 1) {
|
if len(c) < (rLen + hLen + params.BlockSize) {
|
||||||
return nil, ErrInvalidMessage
|
return nil, ErrInvalidMessage
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,10 @@ func (bitCurve *BitCurve) Params() *elliptic.CurveParams {
|
||||||
|
|
||||||
// IsOnCurve returns true if the given (x,y) lies on the BitCurve.
|
// IsOnCurve returns true if the given (x,y) lies on the BitCurve.
|
||||||
func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
|
func (bitCurve *BitCurve) IsOnCurve(x, y *big.Int) bool {
|
||||||
|
if x.Cmp(bitCurve.P) >= 0 || y.Cmp(bitCurve.P) >= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
// y² = x³ + b
|
// y² = x³ + b
|
||||||
y2 := new(big.Int).Mul(y, y) //y²
|
y2 := new(big.Int).Mul(y, y) //y²
|
||||||
y2.Mod(y2, bitCurve.P) //y²%P
|
y2.Mod(y2, bitCurve.P) //y²%P
|
||||||
|
|
|
||||||
|
|
@ -109,8 +109,10 @@ int secp256k1_ext_scalar_mul(const secp256k1_context* ctx, unsigned char *point,
|
||||||
ARG_CHECK(scalar != NULL);
|
ARG_CHECK(scalar != NULL);
|
||||||
(void)ctx;
|
(void)ctx;
|
||||||
|
|
||||||
secp256k1_fe_set_b32_limit(&feX, point);
|
if (!secp256k1_fe_set_b32_limit(&feX, point) ||
|
||||||
secp256k1_fe_set_b32_limit(&feY, point+32);
|
!secp256k1_fe_set_b32_limit(&feY, point+32)) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
secp256k1_ge_set_xy(&ge, &feX, &feY);
|
secp256k1_ge_set_xy(&ge, &feX, &feY);
|
||||||
secp256k1_scalar_set_b32(&s, scalar, &overflow);
|
secp256k1_scalar_set_b32(&s, scalar, &overflow);
|
||||||
if (overflow || secp256k1_scalar_is_zero(&s)) {
|
if (overflow || secp256k1_scalar_is_zero(&s)) {
|
||||||
|
|
|
||||||
|
|
@ -164,6 +164,13 @@ type btCurve struct {
|
||||||
*secp256k1.KoblitzCurve
|
*secp256k1.KoblitzCurve
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (curve btCurve) IsOnCurve(x, y *big.Int) bool {
|
||||||
|
if x.Cmp(secp256k1.Params().P) >= 0 || y.Cmp(secp256k1.Params().P) >= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return curve.KoblitzCurve.IsOnCurve(x, y)
|
||||||
|
}
|
||||||
|
|
||||||
// Marshal converts a point given as (x, y) into a byte slice.
|
// Marshal converts a point given as (x, y) into a byte slice.
|
||||||
func (curve btCurve) Marshal(x, y *big.Int) []byte {
|
func (curve btCurve) Marshal(x, y *big.Int) []byte {
|
||||||
byteLen := (curve.Params().BitSize + 7) / 8
|
byteLen := (curve.Params().BitSize + 7) / 8
|
||||||
|
|
|
||||||
|
|
@ -114,10 +114,11 @@ type txRequest struct {
|
||||||
// txDelivery is the notification that a batch of transactions have been added
|
// txDelivery is the notification that a batch of transactions have been added
|
||||||
// to the pool and should be untracked.
|
// to the pool and should be untracked.
|
||||||
type txDelivery struct {
|
type txDelivery struct {
|
||||||
origin string // Identifier of the peer originating the notification
|
origin string // Identifier of the peer originating the notification
|
||||||
hashes []common.Hash // Batch of transaction hashes having been delivered
|
hashes []common.Hash // Batch of transaction hashes having been delivered
|
||||||
metas []txMetadata // Batch of metadata associated with the delivered hashes
|
metas []txMetadata // Batch of metadata associated with the delivered hashes
|
||||||
direct bool // Whether this is a direct reply or a broadcast
|
direct bool // Whether this is a direct reply or a broadcast
|
||||||
|
violation error // Whether we encountered a protocol violation
|
||||||
}
|
}
|
||||||
|
|
||||||
// txDrop is the notification that a peer has disconnected.
|
// txDrop is the notification that a peer has disconnected.
|
||||||
|
|
@ -285,6 +286,7 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
||||||
knownMeter = txReplyKnownMeter
|
knownMeter = txReplyKnownMeter
|
||||||
underpricedMeter = txReplyUnderpricedMeter
|
underpricedMeter = txReplyUnderpricedMeter
|
||||||
otherRejectMeter = txReplyOtherRejectMeter
|
otherRejectMeter = txReplyOtherRejectMeter
|
||||||
|
violation error
|
||||||
)
|
)
|
||||||
if !direct {
|
if !direct {
|
||||||
inMeter = txBroadcastInMeter
|
inMeter = txBroadcastInMeter
|
||||||
|
|
@ -331,6 +333,12 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
||||||
case errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) || errors.Is(err, txpool.ErrTxGasPriceTooLow):
|
case errors.Is(err, txpool.ErrUnderpriced) || errors.Is(err, txpool.ErrReplaceUnderpriced) || errors.Is(err, txpool.ErrTxGasPriceTooLow):
|
||||||
underpriced++
|
underpriced++
|
||||||
|
|
||||||
|
case errors.Is(err, txpool.ErrKZGVerificationError):
|
||||||
|
// KZG verification failed, terminate transaction processing immediately.
|
||||||
|
// Since KZG verification is computationally expensive, this acts as a
|
||||||
|
// defensive measure against potential DoS attacks.
|
||||||
|
violation = err
|
||||||
|
|
||||||
default:
|
default:
|
||||||
otherreject++
|
otherreject++
|
||||||
}
|
}
|
||||||
|
|
@ -339,6 +347,11 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
||||||
kind: batch[j].Type(),
|
kind: batch[j].Type(),
|
||||||
size: uint32(batch[j].Size()),
|
size: uint32(batch[j].Size()),
|
||||||
})
|
})
|
||||||
|
// Terminate the transaction processing if violation is encountered. All
|
||||||
|
// the remaining transactions in response will be silently discarded.
|
||||||
|
if violation != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
knownMeter.Mark(duplicate)
|
knownMeter.Mark(duplicate)
|
||||||
underpricedMeter.Mark(underpriced)
|
underpricedMeter.Mark(underpriced)
|
||||||
|
|
@ -349,9 +362,13 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
log.Debug("Peer delivering stale transactions", "peer", peer, "rejected", otherreject)
|
log.Debug("Peer delivering stale transactions", "peer", peer, "rejected", otherreject)
|
||||||
}
|
}
|
||||||
|
// If we encountered a protocol violation, disconnect this peer.
|
||||||
|
if violation != nil {
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
select {
|
select {
|
||||||
case f.cleanup <- &txDelivery{origin: peer, hashes: added, metas: metas, direct: direct}:
|
case f.cleanup <- &txDelivery{origin: peer, hashes: added, metas: metas, direct: direct, violation: violation}:
|
||||||
return nil
|
return nil
|
||||||
case <-f.quit:
|
case <-f.quit:
|
||||||
return errTerminated
|
return errTerminated
|
||||||
|
|
@ -746,6 +763,11 @@ func (f *TxFetcher) loop() {
|
||||||
// Something was delivered, try to reschedule requests
|
// Something was delivered, try to reschedule requests
|
||||||
f.scheduleFetches(timeoutTimer, timeoutTrigger, nil) // Partial delivery may enable others to deliver too
|
f.scheduleFetches(timeoutTimer, timeoutTrigger, nil) // Partial delivery may enable others to deliver too
|
||||||
}
|
}
|
||||||
|
// If we encountered a protocol violation, disconnect the peer
|
||||||
|
if delivery.violation != nil {
|
||||||
|
log.Warn("Disconnect peer for protocol violation", "peer", delivery.origin, "error", delivery.violation)
|
||||||
|
f.dropPeer(delivery.origin)
|
||||||
|
}
|
||||||
|
|
||||||
case drop := <-f.drop:
|
case drop := <-f.drop:
|
||||||
// A peer was dropped, remove all traces of it
|
// A peer was dropped, remove all traces of it
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@
|
||||||
package fetcher
|
package fetcher
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/sha256"
|
||||||
"errors"
|
"errors"
|
||||||
"math/big"
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
|
@ -28,7 +29,10 @@ import (
|
||||||
"github.com/ethereum/go-ethereum/common/mclock"
|
"github.com/ethereum/go-ethereum/common/mclock"
|
||||||
"github.com/ethereum/go-ethereum/core/txpool"
|
"github.com/ethereum/go-ethereum/core/txpool"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||||
"github.com/ethereum/go-ethereum/params"
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/holiman/uint256"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
@ -1908,6 +1912,114 @@ func TestTransactionFetcherDropAlternates(t *testing.T) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeInvalidBlobTx() *types.Transaction {
|
||||||
|
key, _ := crypto.GenerateKey()
|
||||||
|
blob := &kzg4844.Blob{byte(0xa)}
|
||||||
|
commitment, _ := kzg4844.BlobToCommitment(blob)
|
||||||
|
blobHash := kzg4844.CalcBlobHashV1(sha256.New(), &commitment)
|
||||||
|
cellProof, _ := kzg4844.ComputeCellProofs(blob)
|
||||||
|
|
||||||
|
// Mutate the cell proof
|
||||||
|
cellProof[0][0] = 0x0
|
||||||
|
|
||||||
|
blobtx := &types.BlobTx{
|
||||||
|
ChainID: uint256.MustFromBig(params.MainnetChainConfig.ChainID),
|
||||||
|
Nonce: 0,
|
||||||
|
GasTipCap: uint256.NewInt(100),
|
||||||
|
GasFeeCap: uint256.NewInt(200),
|
||||||
|
Gas: 21000,
|
||||||
|
BlobFeeCap: uint256.NewInt(200),
|
||||||
|
BlobHashes: []common.Hash{blobHash},
|
||||||
|
Value: uint256.NewInt(100),
|
||||||
|
Sidecar: types.NewBlobTxSidecar(types.BlobSidecarVersion1, []kzg4844.Blob{*blob}, []kzg4844.Commitment{commitment}, cellProof),
|
||||||
|
}
|
||||||
|
return types.MustSignNewTx(key, types.LatestSigner(params.MainnetChainConfig), blobtx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test ensures that the peer will be disconnected for protocol violation
|
||||||
|
// and all its internal traces should be removed properly.
|
||||||
|
func TestTransactionProtocolViolation(t *testing.T) {
|
||||||
|
//log.SetDefault(log.NewLogger(log.NewTerminalHandlerWithLevel(os.Stderr, log.LevelDebug, true)))
|
||||||
|
|
||||||
|
var (
|
||||||
|
badTx = makeInvalidBlobTx()
|
||||||
|
drop = make(chan struct{}, 1)
|
||||||
|
)
|
||||||
|
testTransactionFetcherParallel(t, txFetcherTest{
|
||||||
|
init: func() *TxFetcher {
|
||||||
|
return NewTxFetcher(
|
||||||
|
func(common.Hash) bool { return false },
|
||||||
|
func(txs []*types.Transaction) []error {
|
||||||
|
var errs []error
|
||||||
|
for range txs {
|
||||||
|
errs = append(errs, txpool.ErrKZGVerificationError)
|
||||||
|
}
|
||||||
|
return errs
|
||||||
|
},
|
||||||
|
func(a string, b []common.Hash) error {
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
func(peer string) { drop <- struct{}{} },
|
||||||
|
)
|
||||||
|
},
|
||||||
|
steps: []interface{}{
|
||||||
|
// Initial announcement to get something into the waitlist
|
||||||
|
doTxNotify{
|
||||||
|
peer: "A",
|
||||||
|
hashes: []common.Hash{testTxs[0].Hash(), badTx.Hash(), testTxs[1].Hash()},
|
||||||
|
types: []byte{types.LegacyTxType, types.BlobTxType, types.LegacyTxType},
|
||||||
|
sizes: []uint32{uint32(testTxs[0].Size()), uint32(badTx.Size()), uint32(testTxs[1].Size())},
|
||||||
|
},
|
||||||
|
isWaiting(map[string][]announce{
|
||||||
|
"A": {
|
||||||
|
{testTxs[0].Hash(), types.LegacyTxType, uint32(testTxs[0].Size())},
|
||||||
|
{badTx.Hash(), types.BlobTxType, uint32(badTx.Size())},
|
||||||
|
{testTxs[1].Hash(), types.LegacyTxType, uint32(testTxs[1].Size())},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
doWait{time: 0, step: true}, // zero time, but the blob fetching should be scheduled
|
||||||
|
|
||||||
|
isWaiting(map[string][]announce{
|
||||||
|
"A": {
|
||||||
|
{testTxs[0].Hash(), types.LegacyTxType, uint32(testTxs[0].Size())},
|
||||||
|
{testTxs[1].Hash(), types.LegacyTxType, uint32(testTxs[1].Size())},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
isScheduled{
|
||||||
|
tracking: map[string][]announce{
|
||||||
|
"A": {
|
||||||
|
{badTx.Hash(), types.BlobTxType, uint32(badTx.Size())},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fetching: map[string][]common.Hash{
|
||||||
|
"A": {badTx.Hash()},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
doTxEnqueue{
|
||||||
|
peer: "A",
|
||||||
|
txs: []*types.Transaction{badTx},
|
||||||
|
direct: true,
|
||||||
|
},
|
||||||
|
// Some internal traces are left and will be cleaned by a following drop
|
||||||
|
// operation.
|
||||||
|
isWaiting(map[string][]announce{
|
||||||
|
"A": {
|
||||||
|
{testTxs[0].Hash(), types.LegacyTxType, uint32(testTxs[0].Size())},
|
||||||
|
{testTxs[1].Hash(), types.LegacyTxType, uint32(testTxs[1].Size())},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
isScheduled{},
|
||||||
|
doFunc(func() { <-drop }),
|
||||||
|
|
||||||
|
// Simulate the drop operation emitted by the server
|
||||||
|
doDrop("A"),
|
||||||
|
isWaiting(nil),
|
||||||
|
isScheduled{nil, nil, nil},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func testTransactionFetcherParallel(t *testing.T, tt txFetcherTest) {
|
func testTransactionFetcherParallel(t *testing.T, tt txFetcherTest) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
testTransactionFetcher(t, tt)
|
testTransactionFetcher(t, tt)
|
||||||
|
|
|
||||||
57
p2p/rlpx/rlpx_oracle_poc_test.go
Normal file
57
p2p/rlpx/rlpx_oracle_poc_test.go
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
package rlpx
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
|
"github.com/ethereum/go-ethereum/crypto/ecies"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestHandshakeECIESInvalidCurveOracle(t *testing.T) {
|
||||||
|
initKey, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
respKey, err := crypto.GenerateKey()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
init := handshakeState{
|
||||||
|
initiator: true,
|
||||||
|
remote: ecies.ImportECDSAPublic(&respKey.PublicKey),
|
||||||
|
}
|
||||||
|
authMsg, err := init.makeAuthMsg(initKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
packet, err := init.sealEIP8(authMsg)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var recv handshakeState
|
||||||
|
if _, err := recv.readMsg(new(authMsgV4), respKey, bytes.NewReader(packet)); err != nil {
|
||||||
|
t.Fatalf("expected valid packet to decrypt: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tampered := append([]byte(nil), packet...)
|
||||||
|
if len(tampered) < 2+65 {
|
||||||
|
t.Fatalf("unexpected packet length %d", len(tampered))
|
||||||
|
}
|
||||||
|
tampered[2] = 0x04
|
||||||
|
for i := 1; i < 65; i++ {
|
||||||
|
tampered[2+i] = 0x00
|
||||||
|
}
|
||||||
|
|
||||||
|
var recv2 handshakeState
|
||||||
|
_, err = recv2.readMsg(new(authMsgV4), respKey, bytes.NewReader(tampered))
|
||||||
|
if err == nil {
|
||||||
|
t.Fatal("expected decryption failure for invalid curve point")
|
||||||
|
}
|
||||||
|
if !errors.Is(err, ecies.ErrInvalidPublicKey) {
|
||||||
|
t.Fatalf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -19,6 +19,6 @@ package version
|
||||||
const (
|
const (
|
||||||
Major = 1 // Major version component of the current release
|
Major = 1 // Major version component of the current release
|
||||||
Minor = 16 // Minor version component of the current release
|
Minor = 16 // Minor version component of the current release
|
||||||
Patch = 7 // Patch version component of the current release
|
Patch = 9 // Patch version component of the current release
|
||||||
Meta = "stable" // Version metadata to append to the version string
|
Meta = "stable" // Version metadata to append to the version string
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue