From c5113850762195ab0fc2e92b252b4b78b9a2e433 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 04:07:50 +0000 Subject: [PATCH 01/24] fix: EBT-03 --- proto/exocore/oracle/v1/params.proto | 12 ++- x/oracle/keeper/aggregator/context.go | 32 ++++--- x/oracle/keeper/common/types.go | 4 +- x/oracle/keeper/msg_server_create_price.go | 7 +- x/oracle/keeper/validator_update_block.go | 6 +- x/oracle/types/keys.go | 7 +- x/oracle/types/params.pb.go | 98 +++++++++++++++------- 7 files changed, 116 insertions(+), 50 deletions(-) diff --git a/proto/exocore/oracle/v1/params.proto b/proto/exocore/oracle/v1/params.proto index 5f23aefc0..1407a062d 100644 --- a/proto/exocore/oracle/v1/params.proto +++ b/proto/exocore/oracle/v1/params.proto @@ -27,9 +27,19 @@ message Params { // voting power need to reach more than threshold_a/threshold_b int32 threshold_b = 8; // for v1, mode=1, get final price as soon as voting power reach threshold_a/threshold_b - int32 mode = 9; + ConsensusMode mode = 9; // for each round, a validator only allowed to provide at most max_det_id continuos rounds of prices for DS int32 max_det_id = 10; // for each token, only keep max_size_prices round of prices int32 max_size_prices = 11; } + +// ConsensusMode defines the consensus mode for the prices. +enum ConsensusMode { + option (gogoproto.goproto_enum_prefix) = false; + // CONSENSUS_MODE_UNSPECIFIED defines an invalid mode. + CONSENSUS_MODE_UNSPECIFIED = 0 [(gogoproto.enumvalue_customname) = "ConsensusModeUnspecified"]; + // CONSENSUS_MODE_ASAP defines the mode to get final price immediately when the voting power + // exceeds the threshold. + CONSENSUS_MODE_ASAP = 1 [(gogoproto.enumvalue_customname) = "ConsensusModeASAP"]; +} \ No newline at end of file diff --git a/x/oracle/keeper/aggregator/context.go b/x/oracle/keeper/aggregator/context.go index 63379cacf..591f653d5 100644 --- a/x/oracle/keeper/aggregator/context.go +++ b/x/oracle/keeper/aggregator/context.go @@ -23,9 +23,19 @@ type roundInfo struct { nextRoundID uint64 // indicate if this round is open for collecting prices or closed in either condition that success with a consensused price or not // 1: open, 2: closed - status int32 + status roundStatus } +// roundStatus is an enum type to indicate the status of a roundInfo +type roundStatus int32 + +const ( + // roundStatusOpen indicates the round is open for collecting prices + roundStatusOpen roundStatus = iota + 1 + // roundStatusClosed indicates the round is closed, either success with a consensused price or not + roundStatusClosed +) + // AggregatorContext keeps memory cache for state params, validatorset, and updatedthese values as they updated on chain. And it keeps the information to track all tokenFeeders' status and data collection // nolint type AggregatorContext struct { @@ -117,7 +127,7 @@ func (agc *AggregatorContext) checkMsg(msg *types.MsgCreatePrice) error { // check feeder is active feederContext := agc.rounds[msg.FeederID] - if feederContext == nil || feederContext.status != 1 { + if feederContext == nil || feederContext.status != roundStatusOpen { // feederId does not exist or not alive return errors.New("context not exist or not available") } @@ -155,7 +165,7 @@ func (agc *AggregatorContext) FillPrice(msg *types.MsgCreatePrice) (*PriceItemKV if listFilled := feederWorker.do(msg); listFilled != nil { if finalPrice := feederWorker.aggregate(); finalPrice != nil { - agc.rounds[msg.FeederID].status = 2 + agc.rounds[msg.FeederID].status = roundStatusClosed feederWorker.seal() return &PriceItemKV{agc.params.GetTokenFeeder(msg.FeederID).TokenID, types.PriceTimeRound{ Price: finalPrice.String(), @@ -188,12 +198,12 @@ func (agc *AggregatorContext) NewCreatePrice(_ sdk.Context, msg *types.MsgCreate // returns: 1st successful sealed, need to be written to KVStore, 2nd: failed sealed tokenID, use previous price to write to KVStore func (agc *AggregatorContext) SealRound(ctx sdk.Context, force bool) (success []*PriceItemKV, failed []uint64, sealed []uint64) { for feederID, round := range agc.rounds { - if round.status == 1 { + if round.status == roundStatusOpen { feeder := agc.params.GetTokenFeeder(feederID) // TODO: for mode=1, we don't do aggregate() here, since if it donesn't success in the transaction execution stage, it won't success here // but it's not always the same for other modes, switch modes switch common.Mode { - case 1: + case types.ConsensusModeASAP: expired := feeder.EndBlock > 0 && uint64(ctx.BlockHeight()) >= feeder.EndBlock outOfWindow := uint64(ctx.BlockHeight())-round.basedBlock >= uint64(common.MaxNonce) if expired || outOfWindow || force { @@ -201,7 +211,7 @@ func (agc *AggregatorContext) SealRound(ctx sdk.Context, force bool) (success [] if expired { delete(agc.rounds, feederID) } else { - round.status = 2 + round.status = roundStatusClosed } // TODO: optimize operformance sealed = append(sealed, feederID) @@ -251,9 +261,9 @@ func (agc *AggregatorContext) PrepareRoundEndBlock(block uint64) (newRoundFeeder } if left >= uint64(common.MaxNonce) { // since do sealround properly before prepareRound, this only possible happens in node restart, and nonce has been taken care of in kvStore - round.status = 2 + round.status = roundStatusClosed } else { - round.status = 1 + round.status = roundStatusOpen if left == 0 { // set nonce for corresponding feederID for new roud start newRoundFeederIDs = append(newRoundFeederIDs, feederIDUint64) @@ -265,14 +275,14 @@ func (agc *AggregatorContext) PrepareRoundEndBlock(block uint64) (newRoundFeeder if left == 0 { round.basedBlock = latestBasedblock round.nextRoundID = latestNextRoundID - round.status = 1 + round.status = roundStatusOpen // set nonce for corresponding feederID for new roud start newRoundFeederIDs = append(newRoundFeederIDs, feederIDUint64) // drop previous worker delete(agc.aggregators, feederIDUint64) - } else if round.status == 1 && left >= uint64(common.MaxNonce) { + } else if round.status == roundStatusOpen && left >= uint64(common.MaxNonce) { // this shouldn't happen, if do sealround properly before prepareRound, basically for test only - round.status = 2 + round.status = roundStatusClosed // TODO: just modify the status here, since sealRound should do all the related seal actions already when parepare invoked } } diff --git a/x/oracle/keeper/common/types.go b/x/oracle/keeper/common/types.go index 60b4d5c89..e4df3eaf4 100644 --- a/x/oracle/keeper/common/types.go +++ b/x/oracle/keeper/common/types.go @@ -3,6 +3,8 @@ package common import ( "math/big" "sort" + + "github.com/ExocoreNetwork/exocore/x/oracle/types" ) var ( @@ -18,7 +20,7 @@ var ( MaxDetID int32 = 5 // consensus mode: v1: as soon as possbile - Mode int32 = 1 + Mode types.ConsensusMode = types.ConsensusModeASAP ) type Set[T comparable] struct { diff --git a/x/oracle/keeper/msg_server_create_price.go b/x/oracle/keeper/msg_server_create_price.go index 79916f879..7eca28a82 100644 --- a/x/oracle/keeper/msg_server_create_price.go +++ b/x/oracle/keeper/msg_server_create_price.go @@ -10,7 +10,10 @@ import ( sdk "github.com/cosmos/cosmos-sdk/types" ) -const layout = "2006-01-02 15:04:05" +const ( + layout = "2006-01-02 15:04:05" + maxFutureOffset = 5 * time.Second +) // CreatePrice proposes price for new round of specific tokenFeeder func (ms msgServer) CreatePrice(goCtx context.Context, msg *types.MsgCreatePrice) (*types.MsgCreatePriceResponse, error) { @@ -90,7 +93,7 @@ func checkTimestamp(goCtx context.Context, msg *types.MsgCreatePrice) error { if err != nil { return errors.New("timestamp format invalid") } - if now.Add(5 * time.Second).Before(t) { + if now.Add(maxFutureOffset).Before(t) { return errors.New("timestamp is in the future") } } diff --git a/x/oracle/keeper/validator_update_block.go b/x/oracle/keeper/validator_update_block.go index b4c23e37b..5ccb7223d 100644 --- a/x/oracle/keeper/validator_update_block.go +++ b/x/oracle/keeper/validator_update_block.go @@ -11,14 +11,14 @@ import ( func (k Keeper) SetValidatorUpdateBlock(ctx sdk.Context, validatorUpdateBlock types.ValidatorUpdateBlock) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.ValidatorUpdateBlockKey)) b := k.cdc.MustMarshal(&validatorUpdateBlock) - store.Set([]byte{0}, b) + store.Set(types.BlockKey, b) } // GetValidatorUpdateBlock returns validatorUpdateBlock func (k Keeper) GetValidatorUpdateBlock(ctx sdk.Context) (val types.ValidatorUpdateBlock, found bool) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.ValidatorUpdateBlockKey)) - b := store.Get([]byte{0}) + b := store.Get(types.BlockKey) if b == nil { return val, false } @@ -30,5 +30,5 @@ func (k Keeper) GetValidatorUpdateBlock(ctx sdk.Context) (val types.ValidatorUpd // RemoveValidatorUpdateBlock removes validatorUpdateBlock from the store func (k Keeper) RemoveValidatorUpdateBlock(ctx sdk.Context) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefix(types.ValidatorUpdateBlockKey)) - store.Delete([]byte{0}) + store.Delete(types.BlockKey) } diff --git a/x/oracle/types/keys.go b/x/oracle/types/keys.go index 140564af9..8992b1bb2 100644 --- a/x/oracle/types/keys.go +++ b/x/oracle/types/keys.go @@ -14,7 +14,12 @@ const ( MemStoreKey = "mem_oracle" ) -var ParamsKey = []byte{0x11} +var ( + // ParamsKey defines the key to store the params in store + ParamsKey = []byte{0x11} + // BlockKey stores the last validator update block + BlockKey = []byte{0x0} +) func KeyPrefix(p string) []byte { return []byte(p) diff --git a/x/oracle/types/params.pb.go b/x/oracle/types/params.pb.go index 895ca8b75..ced645ef2 100644 --- a/x/oracle/types/params.pb.go +++ b/x/oracle/types/params.pb.go @@ -23,6 +23,35 @@ var _ = math.Inf // proto package needs to be updated. const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package +// ConsensusMode defines the consensus mode for the prices. +type ConsensusMode int32 + +const ( + // CONSENSUS_MODE_UNSPECIFIED defines an invalid mode. + ConsensusModeUnspecified ConsensusMode = 0 + // CONSENSUS_MODE_ASAP defines the mode to get final price immediately when the voting power + // exceeds the threshold. + ConsensusModeASAP ConsensusMode = 1 +) + +var ConsensusMode_name = map[int32]string{ + 0: "CONSENSUS_MODE_UNSPECIFIED", + 1: "CONSENSUS_MODE_ASAP", +} + +var ConsensusMode_value = map[string]int32{ + "CONSENSUS_MODE_UNSPECIFIED": 0, + "CONSENSUS_MODE_ASAP": 1, +} + +func (x ConsensusMode) String() string { + return proto.EnumName(ConsensusMode_name, int32(x)) +} + +func (ConsensusMode) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_72f39bba4594b794, []int{0} +} + // Params defines the parameters for the module. type Params struct { // chains represents the blockchains info @@ -42,7 +71,7 @@ type Params struct { // voting power need to reach more than threshold_a/threshold_b ThresholdB int32 `protobuf:"varint,8,opt,name=threshold_b,json=thresholdB,proto3" json:"threshold_b,omitempty"` // for v1, mode=1, get final price as soon as voting power reach threshold_a/threshold_b - Mode int32 `protobuf:"varint,9,opt,name=mode,proto3" json:"mode,omitempty"` + Mode ConsensusMode `protobuf:"varint,9,opt,name=mode,proto3,enum=exocore.oracle.v1.ConsensusMode" json:"mode,omitempty"` // for each round, a validator only allowed to provide at most max_det_id continuos rounds of prices for DS MaxDetId int32 `protobuf:"varint,10,opt,name=max_det_id,json=maxDetId,proto3" json:"max_det_id,omitempty"` // for each token, only keep max_size_prices round of prices @@ -137,11 +166,11 @@ func (m *Params) GetThresholdB() int32 { return 0 } -func (m *Params) GetMode() int32 { +func (m *Params) GetMode() ConsensusMode { if m != nil { return m.Mode } - return 0 + return ConsensusModeUnspecified } func (m *Params) GetMaxDetId() int32 { @@ -159,39 +188,46 @@ func (m *Params) GetMaxSizePrices() int32 { } func init() { + proto.RegisterEnum("exocore.oracle.v1.ConsensusMode", ConsensusMode_name, ConsensusMode_value) proto.RegisterType((*Params)(nil), "exocore.oracle.v1.Params") } func init() { proto.RegisterFile("exocore/oracle/v1/params.proto", fileDescriptor_72f39bba4594b794) } var fileDescriptor_72f39bba4594b794 = []byte{ - // 410 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0x31, 0x6f, 0xd3, 0x40, - 0x14, 0xc7, 0x6d, 0x92, 0xb8, 0xed, 0x85, 0x0a, 0x71, 0x62, 0x38, 0x4a, 0xb9, 0x56, 0x08, 0xa1, - 0x4e, 0x76, 0x4b, 0x36, 0x36, 0x1a, 0x40, 0x82, 0x21, 0x8a, 0x1c, 0x26, 0x16, 0xeb, 0x62, 0xbf, - 0xc4, 0x56, 0x6c, 0x9f, 0x75, 0x77, 0x0e, 0x26, 0x9f, 0x82, 0x91, 0x91, 0x8f, 0xc3, 0x98, 0x11, - 0x31, 0xa1, 0xe4, 0x8b, 0xa0, 0x3b, 0x3b, 0x01, 0x9a, 0x64, 0x7b, 0xf7, 0xfe, 0xbf, 0x9f, 0xde, - 0xe9, 0xe9, 0x21, 0x0a, 0x15, 0x0f, 0xb9, 0x00, 0x8f, 0x0b, 0x16, 0xa6, 0xe0, 0xcd, 0x6f, 0xbc, - 0x82, 0x09, 0x96, 0x49, 0xb7, 0x10, 0x5c, 0x71, 0xfc, 0xb0, 0xc9, 0xdd, 0x3a, 0x77, 0xe7, 0x37, - 0x67, 0xe7, 0xbb, 0x4a, 0x92, 0x4f, 0x78, 0x2d, 0x9c, 0x3d, 0xdf, 0x4d, 0x15, 0x9f, 0x41, 0x1e, - 0x4c, 0x00, 0x22, 0x10, 0x0d, 0xf5, 0x68, 0xca, 0xa7, 0xdc, 0x94, 0x9e, 0xae, 0xea, 0xee, 0xb3, - 0x5f, 0x2d, 0xe4, 0x0c, 0xcd, 0x74, 0x7c, 0x8d, 0x9c, 0x30, 0x66, 0x49, 0x2e, 0x89, 0x7d, 0xd9, - 0xba, 0xea, 0xbe, 0x24, 0xee, 0xce, 0x47, 0xdc, 0xbe, 0x06, 0xfc, 0x86, 0xd3, 0x86, 0x19, 0x24, - 0xc9, 0xbd, 0x83, 0xc6, 0x47, 0x0d, 0xf8, 0x0d, 0x87, 0x7b, 0xe8, 0x48, 0xf2, 0x52, 0x84, 0x20, - 0x49, 0xcb, 0x28, 0x8f, 0xf7, 0x28, 0x23, 0x43, 0xf8, 0x1b, 0x12, 0xf7, 0x50, 0x47, 0x94, 0x29, - 0x48, 0xd2, 0x36, 0xca, 0xd3, 0x3d, 0x8a, 0x5f, 0xa6, 0xd0, 0x68, 0x35, 0x8b, 0xfb, 0xe8, 0xf4, - 0xdf, 0x25, 0x48, 0xd2, 0x31, 0x32, 0x3d, 0xf4, 0xc5, 0x77, 0x06, 0xf3, 0xef, 0xab, 0xbf, 0x0f, - 0x89, 0x9f, 0xa0, 0x93, 0x8c, 0x55, 0x41, 0xce, 0xf3, 0x10, 0x88, 0x73, 0x69, 0x5f, 0x75, 0xfc, - 0xe3, 0x8c, 0x55, 0x03, 0xfd, 0xc6, 0x17, 0xa8, 0xab, 0x62, 0x01, 0x32, 0xe6, 0x69, 0x14, 0x30, - 0x72, 0x64, 0x62, 0xb4, 0x6d, 0xbd, 0xfe, 0x1f, 0x18, 0x93, 0xe3, 0x3b, 0xc0, 0x2d, 0xc6, 0xa8, - 0x9d, 0xf1, 0x08, 0xc8, 0x89, 0x49, 0x4c, 0x8d, 0xcf, 0x11, 0xd2, 0x23, 0x23, 0x50, 0x41, 0x12, - 0x11, 0xb4, 0x9d, 0xf9, 0x06, 0xd4, 0xfb, 0x08, 0xbf, 0x40, 0x0f, 0x74, 0x2a, 0x93, 0x05, 0x04, - 0x85, 0x48, 0xf4, 0x1e, 0xbb, 0x06, 0x39, 0xcd, 0x58, 0x35, 0x4a, 0x16, 0x30, 0x34, 0xcd, 0x57, - 0xed, 0x6f, 0xdf, 0x2f, 0xac, 0xdb, 0x0f, 0x3f, 0x56, 0xd4, 0x5e, 0xae, 0xa8, 0xfd, 0x7b, 0x45, - 0xed, 0xaf, 0x6b, 0x6a, 0x2d, 0xd7, 0xd4, 0xfa, 0xb9, 0xa6, 0xd6, 0xa7, 0xeb, 0x69, 0xa2, 0xe2, - 0x72, 0xec, 0x86, 0x3c, 0xf3, 0xde, 0xd6, 0x0b, 0x19, 0x80, 0xfa, 0xcc, 0xc5, 0xcc, 0xdb, 0x1c, - 0x53, 0xb5, 0x39, 0x27, 0xf5, 0xa5, 0x00, 0x39, 0x76, 0xcc, 0xbd, 0xf4, 0xfe, 0x04, 0x00, 0x00, - 0xff, 0xff, 0xf0, 0xd5, 0xec, 0x1a, 0xbe, 0x02, 0x00, 0x00, + // 511 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x92, 0xbf, 0x6f, 0xd3, 0x40, + 0x14, 0xc7, 0x6d, 0xf2, 0xa3, 0xed, 0x85, 0x40, 0x7b, 0x80, 0x74, 0x98, 0xe0, 0x5a, 0x08, 0xa1, + 0x88, 0xc1, 0x6e, 0x1b, 0x26, 0xc4, 0x92, 0x26, 0xa9, 0x14, 0xa4, 0xa6, 0x91, 0x4d, 0x16, 0x16, + 0xcb, 0xb1, 0x5f, 0x12, 0xab, 0xb1, 0x2f, 0xba, 0x73, 0x8a, 0xe9, 0xca, 0x82, 0x3a, 0x31, 0xb2, + 0x54, 0x42, 0xe2, 0x9f, 0x61, 0xec, 0xc8, 0x88, 0x92, 0x7f, 0x83, 0x01, 0xf9, 0xec, 0x94, 0xa6, + 0x49, 0xb7, 0xbb, 0xf7, 0xfd, 0x7c, 0xf4, 0x9e, 0x9e, 0x1e, 0x52, 0x21, 0xa6, 0x2e, 0x65, 0x60, + 0x50, 0xe6, 0xb8, 0x63, 0x30, 0xce, 0xf6, 0x8d, 0x89, 0xc3, 0x9c, 0x80, 0xeb, 0x13, 0x46, 0x23, + 0x8a, 0x77, 0xb2, 0x5c, 0x4f, 0x73, 0xfd, 0x6c, 0x5f, 0xa9, 0xac, 0x2a, 0x7e, 0x38, 0xa0, 0xa9, + 0xa0, 0xbc, 0x5c, 0x4d, 0x23, 0x7a, 0x0a, 0xa1, 0x3d, 0x00, 0xf0, 0x80, 0x65, 0xd4, 0xe3, 0x21, + 0x1d, 0x52, 0xf1, 0x34, 0x92, 0x57, 0x5a, 0x7d, 0xf1, 0x37, 0x87, 0x8a, 0x5d, 0xd1, 0x1d, 0xef, + 0xa1, 0xa2, 0x3b, 0x72, 0xfc, 0x90, 0x13, 0x59, 0xcb, 0x55, 0x4b, 0x07, 0x44, 0x5f, 0x19, 0x44, + 0x6f, 0x24, 0x80, 0x99, 0x71, 0x89, 0x21, 0x1a, 0x71, 0x72, 0xef, 0x4e, 0xe3, 0x43, 0x02, 0x98, + 0x19, 0x87, 0x6b, 0x68, 0x83, 0xd3, 0x29, 0x73, 0x81, 0x93, 0x9c, 0x50, 0x9e, 0xae, 0x51, 0x2c, + 0x41, 0x98, 0x0b, 0x12, 0xd7, 0x50, 0x81, 0x4d, 0xc7, 0xc0, 0x49, 0x5e, 0x28, 0xcf, 0xd7, 0x28, + 0xe6, 0x74, 0x0c, 0x99, 0x96, 0xb2, 0xb8, 0x81, 0xca, 0x37, 0x97, 0xc0, 0x49, 0x41, 0xc8, 0xea, + 0x5d, 0x23, 0x1e, 0x09, 0xcc, 0xbc, 0x1f, 0xfd, 0xff, 0x70, 0xfc, 0x0c, 0x6d, 0x05, 0x4e, 0x6c, + 0x87, 0x34, 0x74, 0x81, 0x14, 0x35, 0xb9, 0x5a, 0x30, 0x37, 0x03, 0x27, 0xee, 0x24, 0x7f, 0xbc, + 0x8b, 0x4a, 0xd1, 0x88, 0x01, 0x1f, 0xd1, 0xb1, 0x67, 0x3b, 0x64, 0x43, 0xc4, 0xe8, 0xba, 0x54, + 0x5f, 0x06, 0xfa, 0x64, 0xf3, 0x16, 0x70, 0x88, 0xdf, 0xa0, 0x7c, 0x40, 0x3d, 0x20, 0x5b, 0x9a, + 0x5c, 0x7d, 0x70, 0xa0, 0xad, 0xdb, 0x37, 0x0d, 0x39, 0x84, 0x7c, 0xca, 0x8f, 0xa9, 0x07, 0xa6, + 0xa0, 0x71, 0x05, 0xa1, 0x64, 0x28, 0x0f, 0x22, 0xdb, 0xf7, 0x08, 0xba, 0x9e, 0xaa, 0x09, 0x51, + 0xdb, 0xc3, 0xaf, 0xd0, 0xc3, 0x24, 0xe5, 0xfe, 0x39, 0xd8, 0x13, 0xe6, 0x27, 0x9b, 0x2e, 0x09, + 0xa4, 0x1c, 0x38, 0xb1, 0xe5, 0x9f, 0x43, 0x57, 0x14, 0xdf, 0xe6, 0xbf, 0xff, 0xd8, 0x95, 0x5e, + 0x7f, 0x91, 0x51, 0x79, 0xa9, 0x07, 0x7e, 0x87, 0x94, 0xc6, 0x49, 0xc7, 0x6a, 0x75, 0xac, 0x9e, + 0x65, 0x1f, 0x9f, 0x34, 0x5b, 0x76, 0xaf, 0x63, 0x75, 0x5b, 0x8d, 0xf6, 0x51, 0xbb, 0xd5, 0xdc, + 0x96, 0x94, 0xca, 0xc5, 0xa5, 0x46, 0x96, 0x94, 0x5e, 0xc8, 0x27, 0xe0, 0xfa, 0x03, 0x1f, 0x3c, + 0xac, 0xa3, 0x47, 0xb7, 0xec, 0xba, 0x55, 0xef, 0x6e, 0xcb, 0xca, 0x93, 0x8b, 0x4b, 0x6d, 0x67, + 0x49, 0x4b, 0x02, 0x25, 0xff, 0xf5, 0xa7, 0x2a, 0x1d, 0xbe, 0xff, 0x35, 0x53, 0xe5, 0xab, 0x99, + 0x2a, 0xff, 0x99, 0xa9, 0xf2, 0xb7, 0xb9, 0x2a, 0x5d, 0xcd, 0x55, 0xe9, 0xf7, 0x5c, 0x95, 0x3e, + 0xee, 0x0d, 0xfd, 0x68, 0x34, 0xed, 0xeb, 0x2e, 0x0d, 0x8c, 0x56, 0xba, 0x9d, 0x0e, 0x44, 0x9f, + 0x28, 0x3b, 0x35, 0x16, 0x47, 0x1f, 0x2f, 0xce, 0x3e, 0xfa, 0x3c, 0x01, 0xde, 0x2f, 0x8a, 0xbb, + 0xae, 0xfd, 0x0b, 0x00, 0x00, 0xff, 0xff, 0xf5, 0xbe, 0x10, 0x4f, 0x66, 0x03, 0x00, 0x00, } func (m *Params) Marshal() (dAtA []byte, err error) { @@ -661,7 +697,7 @@ func (m *Params) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - m.Mode |= int32(b&0x7F) << shift + m.Mode |= ConsensusMode(b&0x7F) << shift if b < 0x80 { break } From af54212e1dbb3d807bbda46fbce3d9f2b2afe1e2 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 04:11:23 +0000 Subject: [PATCH 02/24] fix: ENG-04 --- precompiles/assets/IAssets.sol | 4 ++-- x/operator/keeper/operator.go | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/precompiles/assets/IAssets.sol b/precompiles/assets/IAssets.sol index e348eeaa1..4263f1aaa 100644 --- a/precompiles/assets/IAssets.sol +++ b/precompiles/assets/IAssets.sol @@ -15,7 +15,7 @@ interface IAssets { /// TRANSACTIONS /// @dev deposit the client chain assets for the staker, - /// that will change the state in deposit module + /// that will change the state in assets module /// Note that this address cannot be a module account. /// @param clientChainID is the layerZero chainID if it is supported. // It might be allocated by Exocore when the client chain isn't supported @@ -27,7 +27,7 @@ interface IAssets { external returns (bool success, uint256 latestAssetState); - /// @dev withdraw To the staker, that will change the state in withdraw module + /// @dev withdraw To the staker, that will change the state in assets module /// Note that this address cannot be a module account. /// @param clientChainID is the layerZero chainID if it is supported. // It might be allocated by Exocore when the client chain isn't supported diff --git a/x/operator/keeper/operator.go b/x/operator/keeper/operator.go index 55361bdce..86a9975ad 100644 --- a/x/operator/keeper/operator.go +++ b/x/operator/keeper/operator.go @@ -27,10 +27,11 @@ func (k *Keeper) SetOperatorInfo( return errorsmod.Wrap(err, "SetOperatorInfo: error occurred when parse acc address from Bech32") } // if already registered, this request should go to EditOperator. + // TODO: EditOperator needs to be implemented. if k.IsOperator(ctx, opAccAddr) { return errorsmod.Wrap( operatortypes.ErrOperatorAlreadyExists, - fmt.Sprintf("SetOperatorInfo: operator already exists, address: %suite", opAccAddr), + fmt.Sprintf("SetOperatorInfo: operator already exists, address: %s", opAccAddr), ) } // TODO: add minimum commission rate module parameter and check that commission exceeds it. From 6be76fb038fa70db99a39833f68509ad3e9b9e5d Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 04:39:11 +0000 Subject: [PATCH 03/24] fix: ENG-06 --- precompiles/bls/methods.go | 30 +++++++++++++++--------------- x/assets/keeper/bank.go | 2 +- x/operator/keeper/usd_value.go | 4 ++-- 3 files changed, 18 insertions(+), 18 deletions(-) diff --git a/precompiles/bls/methods.go b/precompiles/bls/methods.go index 2b3694668..4e3e5b3f3 100644 --- a/precompiles/bls/methods.go +++ b/precompiles/bls/methods.go @@ -29,8 +29,8 @@ func (p Precompile) Verify( method *abi.Method, args []interface{}, ) ([]byte, error) { - if len(args) != 3 { - return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 3, len(args)) + if len(args) != len(p.ABI.Methods[MethodVerify].Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodVerify].Inputs), len(args)) } sigBz, ok := args[1].([]byte) @@ -64,8 +64,8 @@ func (p Precompile) FastAggregateVerify( method *abi.Method, args []interface{}, ) ([]byte, error) { - if len(args) != 3 { - return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 3, len(args)) + if len(args) != len(p.ABI.Methods[MethodFastAggregateVerify].Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodFastAggregateVerify].Inputs), len(args)) } sigBz, ok := args[1].([]byte) @@ -102,8 +102,8 @@ func (p Precompile) GeneratePrivateKey( method *abi.Method, args []interface{}, ) ([]byte, error) { - if len(args) != 0 { - return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 3, len(args)) + if len(args) != len(p.ABI.Methods[MethodGeneratePrivateKey].Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodGeneratePrivateKey].Inputs), len(args)) } privkey, err := blst.RandKey() @@ -138,8 +138,8 @@ func (p Precompile) Sign( method *abi.Method, args []interface{}, ) ([]byte, error) { - if len(args) != 2 { - return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 3, len(args)) + if len(args) != len(p.ABI.Methods[MethodSign].Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodSign].Inputs), len(args)) } privkeyBz, ok := args[0].([]byte) @@ -163,8 +163,8 @@ func (p Precompile) AggregatePubkeys( method *abi.Method, args []interface{}, ) ([]byte, error) { - if len(args) != 1 { - return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 3, len(args)) + if len(args) != len(p.ABI.Methods[MethodAggregatePubkeys].Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodAggregatePubkeys].Inputs), len(args)) } pubkeysBz, ok := args[0].([][]byte) @@ -184,8 +184,8 @@ func (p Precompile) AggregateSignatures( method *abi.Method, args []interface{}, ) ([]byte, error) { - if len(args) != 1 { - return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 3, len(args)) + if len(args) != len(p.ABI.Methods[MethodAggregateSignatures].Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodAggregateSignatures].Inputs), len(args)) } sigsBz, ok := args[0].([][]byte) @@ -195,7 +195,7 @@ func (p Precompile) AggregateSignatures( aggregatedSig, err := blst.AggregateCompressedSignatures(sigsBz) if err != nil { - return nil, fmt.Errorf("failed to aggregate public keys") + return nil, fmt.Errorf("failed to aggregate signatures") } return method.Outputs.Pack(aggregatedSig.Marshal()) @@ -205,8 +205,8 @@ func (p Precompile) AddTwoPubkeys( method *abi.Method, args []interface{}, ) ([]byte, error) { - if len(args) != 2 { - return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, 2, len(args)) + if len(args) != len(p.ABI.Methods[MethodAddTwoPubkeys].Inputs) { + return nil, fmt.Errorf(cmn.ErrInvalidNumberOfArgs, len(p.ABI.Methods[MethodAddTwoPubkeys].Inputs), len(args)) } pubkeyOneBz, ok := args[0].([]byte) diff --git a/x/assets/keeper/bank.go b/x/assets/keeper/bank.go index 75960bce2..189afc807 100644 --- a/x/assets/keeper/bank.go +++ b/x/assets/keeper/bank.go @@ -23,7 +23,7 @@ type DepositWithdrawParams struct { func (k Keeper) PerformDepositOrWithdraw(ctx sdk.Context, params *DepositWithdrawParams) error { // check params parameter before executing deposit operation if params.OpAmount.IsNegative() { - return errorsmod.Wrap(assetstypes.ErrInvalidDepositAmount, fmt.Sprintf("negative deposit amount:%s", params.OpAmount)) + return errorsmod.Wrap(assetstypes.ErrInvalidDepositAmount, fmt.Sprintf("negative amount:%s", params.OpAmount)) } stakeID, assetID := assetstypes.GetStakeIDAndAssetID(params.ClientChainLzID, params.StakerAddress, params.AssetsAddress) diff --git a/x/operator/keeper/usd_value.go b/x/operator/keeper/usd_value.go index ee1009948..1381170b0 100644 --- a/x/operator/keeper/usd_value.go +++ b/x/operator/keeper/usd_value.go @@ -60,7 +60,7 @@ func (k *Keeper) InitOperatorUSDValue(ctx sdk.Context, avsAddr, operatorAddr str store := prefix.NewStore(ctx.KVStore(k.storeKey), operatortypes.KeyPrefixUSDValueForOperator) var key []byte if operatorAddr == "" { - return errorsmod.Wrap(operatortypes.ErrParameterInvalid, "UpdateOperatorUSDValue the operatorAddr is empty") + return errorsmod.Wrap(operatortypes.ErrParameterInvalid, "InitOperatorUSDValue the operatorAddr is empty") } key = assetstype.GetJoinedStoreKey(avsAddr, operatorAddr) if store.Has(key) { @@ -85,7 +85,7 @@ func (k *Keeper) DeleteOperatorUSDValue(ctx sdk.Context, avsAddr, operatorAddr s store := prefix.NewStore(ctx.KVStore(k.storeKey), operatortypes.KeyPrefixUSDValueForOperator) var key []byte if operatorAddr == "" { - return errorsmod.Wrap(operatortypes.ErrParameterInvalid, "UpdateOperatorUSDValue the operatorAddr is empty") + return errorsmod.Wrap(operatortypes.ErrParameterInvalid, "DeleteOperatorUSDValue the operatorAddr is empty") } key = assetstype.GetJoinedStoreKey(avsAddr, operatorAddr) store.Delete(key) From 7f3eca09b9b65eb219da1381e14e562425437aff Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 04:40:27 +0000 Subject: [PATCH 04/24] fix: ENO-02 --- x/assets/types/general.go | 2 +- x/dogfood/types/genesis.go | 2 +- x/oracle/keeper/aggregator/aggregator.go | 2 +- x/reward/keeper/claim_reward.go | 2 +- x/reward/keeper/claim_reward_test.go | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x/assets/types/general.go b/x/assets/types/general.go index 3b95b7809..fd75c0934 100644 --- a/x/assets/types/general.go +++ b/x/assets/types/general.go @@ -40,7 +40,7 @@ const ( const ( Deposit CrossChainOpType = iota WithdrawPrincipal - WithDrawReward + WithdrawReward DelegateTo UndelegateFrom Slash diff --git a/x/dogfood/types/genesis.go b/x/dogfood/types/genesis.go index 79056b074..440cd2a07 100644 --- a/x/dogfood/types/genesis.go +++ b/x/dogfood/types/genesis.go @@ -117,7 +117,7 @@ func (gs GenesisState) Validate() error { } // we don't know the current epoch, since this is stateless validation. - // to check epoochs aren't duplicated. + // to check epochs aren't duplicated. epochs := make(map[int64]struct{}, len(gs.OptOutExpiries)) // to check that there is no duplicate address - not by per epoch but overall. addrsMap := make(map[string]struct{}) diff --git a/x/oracle/keeper/aggregator/aggregator.go b/x/oracle/keeper/aggregator/aggregator.go index 5da005a60..48e7778cd 100644 --- a/x/oracle/keeper/aggregator/aggregator.go +++ b/x/oracle/keeper/aggregator/aggregator.go @@ -38,7 +38,7 @@ func (r *reportPrice) aggregate() *big.Int { type aggregator struct { finalPrice *big.Int reports []*reportPrice - // total valiadtor power who has submitted pice + // total valiadtor power who has submitted price reportPower *big.Int totalPower *big.Int // validator set total power diff --git a/x/reward/keeper/claim_reward.go b/x/reward/keeper/claim_reward.go index 6e4156823..c55a6b0b6 100644 --- a/x/reward/keeper/claim_reward.go +++ b/x/reward/keeper/claim_reward.go @@ -39,7 +39,7 @@ func getRewardParamsFromEventLog(log *ethtypes.Log) (*RewardParams, error) { if err != nil { return nil, errorsmod.Wrap(err, "error occurred when binary read action") } - if action != types.WithDrawReward { + if action != types.WithdrawReward { // not handle the actions that isn't deposit return nil, nil } diff --git a/x/reward/keeper/claim_reward_test.go b/x/reward/keeper/claim_reward_test.go index 5a275401e..410ec6226 100644 --- a/x/reward/keeper/claim_reward_test.go +++ b/x/reward/keeper/claim_reward_test.go @@ -16,7 +16,7 @@ func (suite *RewardTestSuite) TestClaimWithdrawRequest() { usdcAddress := common.HexToAddress("0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48") event := &keeper.RewardParams{ ClientChainLzID: 101, - Action: types.WithDrawReward, + Action: types.WithdrawReward, WithdrawRewardAddress: suite.Address[:], OpAmount: sdkmath.NewInt(10), } From 8b5aebe89fa9ef3b3d343a65085916b41239f539 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 04:49:30 +0000 Subject: [PATCH 05/24] fix: ENO-03 --- x/assets/keeper/client_chain_asset.go | 1 + x/assets/types/errors.go | 5 +---- x/delegation/keeper/delegation.go | 2 +- x/delegation/keeper/delegation_state.go | 4 ++-- x/delegation/types/errors.go | 2 +- x/operator/types/errors.go | 23 +++++++++-------------- x/reward/types/errors.go | 1 - 7 files changed, 15 insertions(+), 23 deletions(-) diff --git a/x/assets/keeper/client_chain_asset.go b/x/assets/keeper/client_chain_asset.go index 6e399ca69..3dff7a31c 100644 --- a/x/assets/keeper/client_chain_asset.go +++ b/x/assets/keeper/client_chain_asset.go @@ -36,6 +36,7 @@ func (k Keeper) UpdateStakingAssetTotalAmount(ctx sdk.Context, assetID string, c // SetStakingAssetInfo todo: Temporarily use clientChainAssetAddr+'_'+LayerZeroChainID as the key. // It provides a function to register the client chain assets supported by exoCore.It's called by genesis configuration now,however it will be called by the governance in the future +// The caller is responsible for ensuring that no such asset already exists (if a new asset is being created) func (k Keeper) SetStakingAssetInfo(ctx sdk.Context, info *assetstype.StakingAssetInfo) (err error) { if info.AssetBasicInfo.Decimals > assetstype.MaxDecimal { return errorsmod.Wrapf(assetstype.ErrInvalidInputParameter, "the decimal is greater than the MaxDecimal,decimal:%v,MaxDecimal:%v", info.AssetBasicInfo.Decimals, assetstype.MaxDecimal) diff --git a/x/assets/types/errors.go b/x/assets/types/errors.go index e65862e6f..38bf37ebb 100644 --- a/x/assets/types/errors.go +++ b/x/assets/types/errors.go @@ -88,12 +88,9 @@ var ( ErrInvalidOperationType = errorsmod.Register( ModuleName, 18, "the operation type is invalid") - ErrRegisterDuplicateAssetID = errorsmod.Register( - ModuleName, 19, - "register new asset with an existing assetID") ErrParseJoinedKey = errorsmod.Register( - ModuleName, 20, + ModuleName, 19, "the joined key can't be parsed", ) ) diff --git a/x/delegation/keeper/delegation.go b/x/delegation/keeper/delegation.go index 0dd37d1a9..86aeb12c2 100644 --- a/x/delegation/keeper/delegation.go +++ b/x/delegation/keeper/delegation.go @@ -233,7 +233,7 @@ func (k *Keeper) DissociateOperatorFromStaker( } oldOperatorAccAddr, err := sdk.AccAddressFromBech32(associatedOperator) if err != nil { - return delegationtype.OperatorAddrIsNotAccAddr + return delegationtype.ErrOperatorAddrIsNotAccAddr } opFunc := func(keys *delegationtype.SingleDelegationInfoReq, amounts *delegationtype.DelegationAmounts) error { diff --git a/x/delegation/keeper/delegation_state.go b/x/delegation/keeper/delegation_state.go index 747c08248..10f3803e8 100644 --- a/x/delegation/keeper/delegation_state.go +++ b/x/delegation/keeper/delegation_state.go @@ -151,7 +151,7 @@ func (k Keeper) UpdateDelegationState(ctx sdk.Context, stakerID, assetID, opAddr // check operator address validation _, err := sdk.AccAddressFromBech32(opAddr) if err != nil { - return shareIsZero, delegationtype.OperatorAddrIsNotAccAddr + return shareIsZero, delegationtype.ErrOperatorAddrIsNotAccAddr } singleStateKey := assetstype.GetJoinedStoreKey(stakerID, assetID, opAddr) delegationState := delegationtype.DelegationAmounts{ @@ -358,7 +358,7 @@ func (k Keeper) DelegationStateByOperatorAssets(ctx sdk.Context, operatorAddr st func (k *Keeper) SetAssociatedOperator(ctx sdk.Context, stakerID, operatorAddr string) error { _, err := sdk.AccAddressFromBech32(operatorAddr) if err != nil { - return delegationtype.OperatorAddrIsNotAccAddr + return delegationtype.ErrOperatorAddrIsNotAccAddr } store := prefix.NewStore(ctx.KVStore(k.storeKey), delegationtype.KeyPrefixAssociatedOperatorByStaker) store.Set([]byte(stakerID), []byte(operatorAddr)) diff --git a/x/delegation/types/errors.go b/x/delegation/types/errors.go index 723ad5a3d..e3d3270e4 100644 --- a/x/delegation/types/errors.go +++ b/x/delegation/types/errors.go @@ -22,7 +22,7 @@ var ( "the amount isn't positive", ) - OperatorAddrIsNotAccAddr = errorsmod.Register( + ErrOperatorAddrIsNotAccAddr = errorsmod.Register( ModuleName, 6, "the operator address isn't a valid acc addr", ) diff --git a/x/operator/types/errors.go b/x/operator/types/errors.go index 49f6be282..feae14baf 100644 --- a/x/operator/types/errors.go +++ b/x/operator/types/errors.go @@ -62,53 +62,48 @@ var ( "the genesis data supplied is invalid", ) - ErrInvalidAvsAddr = errorsmod.Register( - ModuleName, 14, - "avs address should be a hex evm contract address", - ) - ErrOperatorAlreadyExists = errorsmod.Register( - ModuleName, 15, + ModuleName, 14, "operator already exists", ) ErrUnknownChainID = errorsmod.Register( - ModuleName, 16, + ModuleName, 15, "unknown chain id", ) ErrOperatorNotRemovingKey = errorsmod.Register( - ModuleName, 17, + ModuleName, 16, "operator not removing key", ) ErrInvalidSlashPower = errorsmod.Register( - ModuleName, 18, + ModuleName, 17, "the slash power is invalid", ) ErrKeyAlreadyExist = errorsmod.Register( - ModuleName, 19, + ModuleName, 18, "the key already exists", ) ErrValueIsNilOrZero = errorsmod.Register( - ModuleName, 20, + ModuleName, 19, "the value is nil or zero", ) ErrKeyNotExistInMap = errorsmod.Register( - ModuleName, 21, + ModuleName, 20, "the key doesn't exist in the map", ) ErrNoSuchAvs = errorsmod.Register( - ModuleName, 22, + ModuleName, 21, "no such avs", ) ErrInvalidConsKey = errorsmod.Register( - ModuleName, 23, + ModuleName, 22, "invalid consensus key", ) ) diff --git a/x/reward/types/errors.go b/x/reward/types/errors.go index 7669f8a77..82e538e7b 100644 --- a/x/reward/types/errors.go +++ b/x/reward/types/errors.go @@ -8,7 +8,6 @@ import ( // x/reward module sentinel errors var ( - ErrSample = errorsmod.Register(ModuleName, 1100, "sample error") ErrInvalidEvmAddressFormat = errorsmod.Register(ModuleName, 2, "the evm address format is error") ErrInvalidLzUaTopicIDLength = errorsmod.Register(ModuleName, 3, "the LZUaTopicID length isn't equal to HashLength") ErrNoParamsKey = errorsmod.Register(ModuleName, 4, "there is no stored key for params") From 1db91e8e83a8ddc9c94187a37d791e92d45be36b Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 04:53:33 +0000 Subject: [PATCH 06/24] fix: IGE-01 --- x/avs/types/keys.go | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/x/avs/types/keys.go b/x/avs/types/keys.go index a13eba2b8..704af1445 100644 --- a/x/avs/types/keys.go +++ b/x/avs/types/keys.go @@ -17,11 +17,17 @@ const ( RouterKey = ModuleName // MemStoreKey defines the in-memory store key - MemStoreKey = "mem_avs" - prefixAVSInfo = iota + 1 + MemStoreKey = "mem_avs" +) + +const ( + // prefixAVSInfo is the prefix for the AVS info store. It starts with a value of 5 + // for backward compatibility with the previous prefix used for the AVS info store. + // at the time of a chain-id upgrade, this may be reset to 1. + prefixAVSInfo = iota + 5 prefixAVSOperatorInfo prefixParams - prefixAVSTaskInfo = iota + 1 + prefixAVSTaskInfo prefixOperatePub prefixAVSAddressToChainID ) From cfaa6d664d7128ed157d1804102a844fcd600d85 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 05:28:47 +0000 Subject: [PATCH 07/24] fix: NBT-01 --- x/dogfood/keeper/keeper.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/dogfood/keeper/keeper.go b/x/dogfood/keeper/keeper.go index 78bab4f33..1aaf729bd 100644 --- a/x/dogfood/keeper/keeper.go +++ b/x/dogfood/keeper/keeper.go @@ -113,4 +113,5 @@ func (k Keeper) mustValidateFields() { types.PanicIfNil(k.operatorKeeper, "operatorKeeper") types.PanicIfNil(k.delegationKeeper, "delegationKeeper") types.PanicIfNil(k.restakingKeeper, "restakingKeeper") + types.PanicIfNil(k.avsKeeper, "avsKeeper") } From 0362d002d544b6fb776ef34a5393f44114807890 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 05:29:50 +0000 Subject: [PATCH 08/24] fix: URW-01 --- x/slash/keeper/execute_slash.go | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/x/slash/keeper/execute_slash.go b/x/slash/keeper/execute_slash.go index 9b920af0d..3c64a6db0 100644 --- a/x/slash/keeper/execute_slash.go +++ b/x/slash/keeper/execute_slash.go @@ -146,11 +146,8 @@ func (k Keeper) PostTxProcessing(ctx sdk.Context, _ core.Message, receipt *ethty topics := [][]common.Hash{ {common.HexToHash(params.ExocoreLzAppEventTopic)}, } - needLogs := filters.FilterLogs(receipt.Logs, nil, nil, addresses, topics) - if err != nil { - return err - } + needLogs := filters.FilterLogs(receipt.Logs, nil, nil, addresses, topics) if len(needLogs) == 0 { log.Println("the hook message doesn't have any event needed to handle") return nil From 100619c46721eb4fcd3ee1a416505a40d3c9fd99 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 05:42:14 +0000 Subject: [PATCH 09/24] chore: lint --- x/oracle/keeper/validator_update_block.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/oracle/keeper/validator_update_block.go b/x/oracle/keeper/validator_update_block.go index 5ccb7223d..e5e6ea96d 100644 --- a/x/oracle/keeper/validator_update_block.go +++ b/x/oracle/keeper/validator_update_block.go @@ -1,4 +1,3 @@ -//nolint:dupl package keeper import ( From 92a4772b7c1bae0450f3024033d7bfa73d322074 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 05:42:55 +0000 Subject: [PATCH 10/24] fix: TRO-01 --- x/avs/keeper/avs.go | 1 + 1 file changed, 1 insertion(+) diff --git a/x/avs/keeper/avs.go b/x/avs/keeper/avs.go index 0ae2b0b18..8accf717c 100644 --- a/x/avs/keeper/avs.go +++ b/x/avs/keeper/avs.go @@ -85,6 +85,7 @@ func (k *Keeper) GetAVSInfoByTaskAddress(ctx sdk.Context, taskAddr string) types k.IterateAVSInfo(ctx, func(_ int64, avsInfo types.AVSInfo) (stop bool) { if taskAddr == avsInfo.GetTaskAddr() { avs = avsInfo + return true // stop because we found the AVS } return false }) From 59102dbeeed72c378a51addcb3f51b539098f4aa Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 06:08:21 +0000 Subject: [PATCH 11/24] fix: RTE-01 --- app/ante/sigverify.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/app/ante/sigverify.go b/app/ante/sigverify.go index 01c99c5b8..bb873d548 100644 --- a/app/ante/sigverify.go +++ b/app/ante/sigverify.go @@ -64,15 +64,26 @@ func ConsumeMultisignatureVerificationGas( meter sdk.GasMeter, sig *signing.MultiSignatureData, pubkey multisig.PubKey, params authtypes.Params, accSeq uint64, ) error { + pubkeys := pubkey.GetPubKeys() size := sig.BitArray.Count() + // each pubkey must either sign, or not sign + if size != len(pubkeys) { + return errorsmod.Wrapf(errortypes.ErrInvalidPubKey, "bitarray length doesn't match the number of public keys") + } + // when not signed, the corresponding signature will not be included; + // in other words, len(sig.Signatures) <= size == len(pubkeys) + if len(sig.Signatures) > size { + return errorsmod.Wrapf(errortypes.ErrTooManySignatures, "number of signatures exceeds number of public keys") + } sigIndex := 0 for i := 0; i < size; i++ { if !sig.BitArray.GetIndex(i) { + // not signed continue } sigV2 := signing.SignatureV2{ - PubKey: pubkey.GetPubKeys()[i], + PubKey: pubkeys[i], Data: sig.Signatures[sigIndex], Sequence: accSeq, } From 3916d7865641f00383f4f399792c638897619803 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 06:09:46 +0000 Subject: [PATCH 12/24] fix: NKR-01 evmos/evmos#2559 --- x/evm/keeper/state_transition.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index bf445b8b3..8621155a3 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -225,7 +225,6 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t commit() // Since the post-processing can alter the log, we need to update the result res.Logs = types.NewLogsFromEth(receipt.Logs) - ctx.EventManager().EmitEvents(tmpCtx.EventManager().Events()) } } From 291fc45de4de536730bfce661afd6422103d16ef Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 06:22:25 +0000 Subject: [PATCH 13/24] fix: EIO-02 --- x/delegation/keeper/abci.go | 46 ++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/x/delegation/keeper/abci.go b/x/delegation/keeper/abci.go index bbc742d56..5d7c6aa3d 100644 --- a/x/delegation/keeper/abci.go +++ b/x/delegation/keeper/abci.go @@ -13,15 +13,14 @@ import ( // EndBlock : completed Undelegation events according to the canCompleted blockHeight // This function will be triggered at the end of every block, it will query the undelegation state to get the records that need to be handled and try to complete the undelegation task. -func (k *Keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - records, err := k.GetPendingUndelegationRecords(ctx, uint64(ctx.BlockHeight())) - if err != nil { - panic(err) - } +func (k *Keeper) EndBlock(oCtx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { + // #nosec G703 // the error is always nil + records, _ := k.GetPendingUndelegationRecords(oCtx, uint64(oCtx.BlockHeight())) if len(records) == 0 { return []abci.ValidatorUpdate{} } for _, record := range records { + ctx, writeCache := oCtx.CacheContext() // check if the operator has been slashed or frozen operatorAccAddress := sdk.MustAccAddressFromBech32(record.OperatorAddr) // todo: don't think about freezing the operator in current implementation @@ -44,15 +43,12 @@ func (k *Keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Valida // #nosec G701 record.CompleteBlockNumber = uint64(ctx.BlockHeight()) + 1 // we need to store two things here: one is the updated record in itself - recordKey, err := k.SetSingleUndelegationRecord(ctx, record) - if err != nil { - panic(err) - } + // #nosec G703 // the error is always nil + recordKey, _ := k.SetSingleUndelegationRecord(ctx, record) // and the other is the fact that it matures at the next block - err = k.StorePendingUndelegationRecord(ctx, recordKey, record) - if err != nil { - panic(err) - } + // #nosec G703 // the error is always nil + _ = k.StorePendingUndelegationRecord(ctx, recordKey, record) + writeCache() continue } // TODO(mike): ensure that operator is required to perform self delegation to match above. @@ -62,10 +58,11 @@ func (k *Keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Valida deltaAmount := &types.DeltaDelegationAmounts{ WaitUndelegationAmount: recordAmountNeg, } - _, err = k.UpdateDelegationState(ctx, record.StakerID, record.AssetID, record.OperatorAddr, deltaAmount) + _, err := k.UpdateDelegationState(ctx, record.StakerID, record.AssetID, record.OperatorAddr, deltaAmount) if err != nil { - // todo: using cached context to remove the panic - panic(err) + // use oCtx so that the error is logged on the original context + k.Logger(oCtx).Error("failed to update delegation state", "error", err) + continue } // update the staker state @@ -73,7 +70,8 @@ func (k *Keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Valida parsedStakerID := strings.Split(record.StakerID, "_") stakerAddr := sdk.AccAddress(hexutil.MustDecode(parsedStakerID[0])) if err := k.bankKeeper.UndelegateCoinsFromModuleToAccount(ctx, types.DelegatedPoolName, stakerAddr, sdk.NewCoins(sdk.NewCoin(assetstypes.NativeAssetDenom, record.ActualCompletedAmount))); err != nil { - panic(err) + k.Logger(oCtx).Error("failed to undelegate coins from module to account", "error", err) + continue } } else { err = k.assetsKeeper.UpdateStakerAssetState(ctx, record.StakerID, record.AssetID, assetstypes.DeltaStakerSingleAsset{ @@ -81,7 +79,8 @@ func (k *Keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Valida PendingUndelegationAmount: recordAmountNeg, }) if err != nil { - panic(err) + k.Logger(oCtx).Error("failed to update staker asset state", "error", err) + continue } } @@ -90,14 +89,15 @@ func (k *Keeper) EndBlock(ctx sdk.Context, _ abci.RequestEndBlock) []abci.Valida PendingUndelegationAmount: recordAmountNeg, }) if err != nil { - panic(err) + k.Logger(oCtx).Error("failed to update operator asset state", "error", err) + continue } // delete the Undelegation records that have been complemented - err = k.DeleteUndelegationRecord(ctx, record) - if err != nil { - panic(err) - } + // #nosec G703 // the error is always nil + _ = k.DeleteUndelegationRecord(ctx, record) + // when calling `writeCache`, events are automatically emitted on the parent context + writeCache() } return []abci.ValidatorUpdate{} } From 12e8e62c445e7fde5c11905512ebf1906818c43b Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 06:24:57 +0000 Subject: [PATCH 14/24] fix: EIO-01 --- x/delegation/keeper/abci.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/x/delegation/keeper/abci.go b/x/delegation/keeper/abci.go index 5d7c6aa3d..922da13c3 100644 --- a/x/delegation/keeper/abci.go +++ b/x/delegation/keeper/abci.go @@ -39,6 +39,9 @@ func (k *Keeper) EndBlock(oCtx sdk.Context, _ abci.RequestEndBlock) []abci.Valid recordID := types.GetUndelegationRecordKey(record.BlockNumber, record.LzTxNonce, record.TxHash, record.OperatorAddr) if k.GetUndelegationHoldCount(ctx, recordID) > 0 { + // delete it from state. then rewrite it with the next block + // #nosec G703 // the error is always nil + _ = k.DeleteUndelegationRecord(ctx, record) // store it again with the next block and move on // #nosec G701 record.CompleteBlockNumber = uint64(ctx.BlockHeight()) + 1 From 9fdb1c4954807d54ff25067d69f5423a1c63b660 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 08:05:46 +0000 Subject: [PATCH 15/24] fix: ENG-03 An AVS can be registered by non-operators as well --- app/app.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/app.go b/app/app.go index b4be93c37..653afdf79 100644 --- a/app/app.go +++ b/app/app.go @@ -663,9 +663,9 @@ func NewExocoreApp( app.GetSubspace(evmtypes.ModuleName), ) - // the AVS manager keeper is the AVS registry. it allows registered operators to add or - // remove AVSs. this keeper is initialized after the EVM keeper because it depends on the EVM keeper - // to set a lookup from codeHash to code, at genesis. + // the AVS manager keeper is the AVS registry. this keeper is initialized after the EVM + // keeper because it depends on the EVM keeper to set a lookup from codeHash to code, + // at genesis. app.AVSManagerKeeper = avsManagerKeeper.NewKeeper( appCodec, keys[avsManagerTypes.StoreKey], &app.OperatorKeeper, From 71befbccd3a323d6f0877a66de9910bc63367106 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:22:29 +0000 Subject: [PATCH 16/24] fix(oracle): don't use Mode as int32 --- x/oracle/keeper/aggregator/info_test.go | 2 +- x/oracle/types/genesis_test.go | 2 +- x/oracle/types/params.go | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/x/oracle/keeper/aggregator/info_test.go b/x/oracle/keeper/aggregator/info_test.go index 72b19f2d2..dbab8f01c 100644 --- a/x/oracle/keeper/aggregator/info_test.go +++ b/x/oracle/keeper/aggregator/info_test.go @@ -55,6 +55,6 @@ var defaultParams = types.Params{ MaxNonce: 3, ThresholdA: 2, ThresholdB: 3, - Mode: 1, + Mode: types.ConsensusModeASAP, MaxDetId: 5, } diff --git a/x/oracle/types/genesis_test.go b/x/oracle/types/genesis_test.go index 09ccb0241..b60dce3b7 100644 --- a/x/oracle/types/genesis_test.go +++ b/x/oracle/types/genesis_test.go @@ -52,7 +52,7 @@ func TestGenesisState_Validate(t *testing.T) { MaxNonce: 3, ThresholdA: 2, ThresholdB: 3, - Mode: 1, + Mode: types.ConsensusModeASAP, MaxDetId: 5, MaxSizePrices: 100, }, diff --git a/x/oracle/types/params.go b/x/oracle/types/params.go index a5b230256..490612d1f 100644 --- a/x/oracle/types/params.go +++ b/x/oracle/types/params.go @@ -33,7 +33,7 @@ func NewParams() Params { // Mode is set to 1 for V1, means: // For deteministic source, use consensus to find out valid final price, for non-deteministic source, use the latest price // Final price will be confirmed as soon as the threshold is reached, and will ignore any furthur messages submitted with prices - Mode: 1, + Mode: ConsensusModeASAP, ThresholdA: 2, ThresholdB: 3, Chains: []*Chain{{}}, @@ -100,7 +100,7 @@ func DefaultParams() Params { ThresholdA: 2, ThresholdB: 3, // V1 set mode to 1 - Mode: 1, + Mode: ConsensusModeASAP, MaxDetId: 5, MaxSizePrices: 100, } @@ -125,7 +125,7 @@ func (p Params) Validate() error { // MaxDetID: This only works for DS, to tell how many continuous roundID_from_DS could be accept at most for one round of exorcore_oracle // ThresholdA/ThresholdB: represents the threshold of voting power to confirm a price as final price // Mode: tells how and when to confirm a final price, expect for voting power threshold, v1 set this value to 1 means final price will be confirmed as soon as it has reached the threshold of total voting power, and just ignore any remaining transactions followed for current round. - if p.MaxNonce < 1 || p.MaxDetId < 1 || p.ThresholdA < 1 || p.ThresholdB < p.ThresholdA || p.Mode != 1 || p.MaxSizePrices < 1 { + if p.MaxNonce < 1 || p.MaxDetId < 1 || p.ThresholdA < 1 || p.ThresholdB < p.ThresholdA || p.Mode != ConsensusModeASAP || p.MaxSizePrices < 1 { return ErrInvalidParams.Wrapf("invalid maxNonce/maxDetID/Threshold/Mode/MaxSizePrices: %d, %d, %d, %d, %d, %d", p.MaxNonce, p.MaxDetId, p.ThresholdA, p.ThresholdB, p.Mode, p.MaxSizePrices) } From fedb3f7b61f1dc1e77c36fba8fc07dc6c8d7648c Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 11:34:23 +0000 Subject: [PATCH 17/24] fix: more validation of lengths in sigverify --- app/ante/sigverify.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/ante/sigverify.go b/app/ante/sigverify.go index bb873d548..e4303d62f 100644 --- a/app/ante/sigverify.go +++ b/app/ante/sigverify.go @@ -66,15 +66,15 @@ func ConsumeMultisignatureVerificationGas( ) error { pubkeys := pubkey.GetPubKeys() size := sig.BitArray.Count() - // each pubkey must either sign, or not sign if size != len(pubkeys) { return errorsmod.Wrapf(errortypes.ErrInvalidPubKey, "bitarray length doesn't match the number of public keys") } - // when not signed, the corresponding signature will not be included; - // in other words, len(sig.Signatures) <= size == len(pubkeys) - if len(sig.Signatures) > size { - return errorsmod.Wrapf(errortypes.ErrTooManySignatures, "number of signatures exceeds number of public keys") + if len(sig.Signatures) != sig.BitArray.NumTrueBitsBefore(size) { + return errorsmod.Wrapf(errortypes.ErrTooManySignatures, "number of signatures exceeds number of bits in bitarray") } + // we have verified that size == len(pubkeys) + // and that the number of signatures == number of true bits in the bitarray + // so we can safely iterate over the pubkeys and signatures sigIndex := 0 for i := 0; i < size; i++ { From e418cbac54dd555832d2a16bf12aea30c09d5607 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Mon, 23 Sep 2024 16:53:25 +0000 Subject: [PATCH 18/24] refactor(delegation): respond to ai comments Instead of ignoring the error (even though we know it to be nil), examine the error and skip the loop if it is not nil. This is preferred over changing the function interface since it is more future proof. --- x/delegation/keeper/abci.go | 147 +++++++++++++-------- x/delegation/keeper/msg_server.go | 70 ++++++---- x/delegation/keeper/un_delegation_state.go | 62 ++++----- x/delegation/types/msg.go | 30 ++++- 4 files changed, 185 insertions(+), 124 deletions(-) diff --git a/x/delegation/keeper/abci.go b/x/delegation/keeper/abci.go index 922da13c3..c8fefe536 100644 --- a/x/delegation/keeper/abci.go +++ b/x/delegation/keeper/abci.go @@ -1,8 +1,6 @@ package keeper import ( - "strings" - assetstypes "github.com/ExocoreNetwork/exocore/x/assets/types" "github.com/ExocoreNetwork/exocore/x/delegation/types" @@ -13,92 +11,133 @@ import ( // EndBlock : completed Undelegation events according to the canCompleted blockHeight // This function will be triggered at the end of every block, it will query the undelegation state to get the records that need to be handled and try to complete the undelegation task. -func (k *Keeper) EndBlock(oCtx sdk.Context, _ abci.RequestEndBlock) []abci.ValidatorUpdate { - // #nosec G703 // the error is always nil - records, _ := k.GetPendingUndelegationRecords(oCtx, uint64(oCtx.BlockHeight())) +func (k *Keeper) EndBlock( + originalCtx sdk.Context, _ abci.RequestEndBlock, +) []abci.ValidatorUpdate { + logger := k.Logger(originalCtx) + records, err := k.GetPendingUndelegationRecords( + originalCtx, uint64(originalCtx.BlockHeight()), + ) + if err != nil { + logger.Error("failed to get pending undelegation records", "error", err) + } if len(records) == 0 { + logger.Info("no pending undelegation records") return []abci.ValidatorUpdate{} } - for _, record := range records { - ctx, writeCache := oCtx.CacheContext() - // check if the operator has been slashed or frozen + for i := range records { + record := records[i] // avoid implicit memory aliasing + ctx, writeCache := originalCtx.CacheContext() + // we can use `Must` here because we stored this record ourselves. operatorAccAddress := sdk.MustAccAddressFromBech32(record.OperatorAddr) - // todo: don't think about freezing the operator in current implementation - /* if k.slashKeeper.IsOperatorFrozen(ctx, operatorAccAddress) { - // reSet the completed height if the operator is frozen - record.CompleteBlockNumber = k.operatorKeeper.GetUnbondingExpirationBlockNumber(ctx, operatorAccAddress, record.BlockNumber) - if record.CompleteBlockNumber <= uint64(ctx.BlockHeight()) { - panic(fmt.Sprintf("the reset completedHeight isn't in future,setHeight:%v,curHeight:%v", record.CompleteBlockNumber, ctx.BlockHeight())) - } - _, innerError = k.SetSingleUndelegationRecord(ctx, record) - if innerError != nil { - panic(innerError) - } - continue - }*/ - - recordID := types.GetUndelegationRecordKey(record.BlockNumber, record.LzTxNonce, record.TxHash, record.OperatorAddr) + // TODO check if the operator has been slashed or frozen + recordID := types.GetUndelegationRecordKey( + record.BlockNumber, record.LzTxNonce, record.TxHash, record.OperatorAddr, + ) if k.GetUndelegationHoldCount(ctx, recordID) > 0 { - // delete it from state. then rewrite it with the next block - // #nosec G703 // the error is always nil - _ = k.DeleteUndelegationRecord(ctx, record) - // store it again with the next block and move on + // delete from all 3 states + if err := k.DeleteUndelegationRecord(ctx, record); err != nil { + logger.Error("failed to delete undelegation record", "error", err) + continue + } + // add back to all 3 states, with the new block height // #nosec G701 record.CompleteBlockNumber = uint64(ctx.BlockHeight()) + 1 - // we need to store two things here: one is the updated record in itself - // #nosec G703 // the error is always nil - recordKey, _ := k.SetSingleUndelegationRecord(ctx, record) - // and the other is the fact that it matures at the next block - // #nosec G703 // the error is always nil - _ = k.StorePendingUndelegationRecord(ctx, recordKey, record) + if err := k.SetUndelegationRecords( + ctx, []types.UndelegationRecord{*record}, + ); err != nil { + logger.Error("failed to set undelegation records", "error", err) + continue + } writeCache() continue } - // TODO(mike): ensure that operator is required to perform self delegation to match above. recordAmountNeg := record.Amount.Neg() // update delegation state deltaAmount := &types.DeltaDelegationAmounts{ WaitUndelegationAmount: recordAmountNeg, } - _, err := k.UpdateDelegationState(ctx, record.StakerID, record.AssetID, record.OperatorAddr, deltaAmount) - if err != nil { + if _, err := k.UpdateDelegationState( + ctx, record.StakerID, record.AssetID, record.OperatorAddr, deltaAmount, + ); err != nil { // use oCtx so that the error is logged on the original context - k.Logger(oCtx).Error("failed to update delegation state", "error", err) + logger.Error( + "failed to update delegation state", + "error", err, + ) continue } // update the staker state if record.AssetID == assetstypes.NativeAssetID { - parsedStakerID := strings.Split(record.StakerID, "_") - stakerAddr := sdk.AccAddress(hexutil.MustDecode(parsedStakerID[0])) - if err := k.bankKeeper.UndelegateCoinsFromModuleToAccount(ctx, types.DelegatedPoolName, stakerAddr, sdk.NewCoins(sdk.NewCoin(assetstypes.NativeAssetDenom, record.ActualCompletedAmount))); err != nil { - k.Logger(oCtx).Error("failed to undelegate coins from module to account", "error", err) + stakerAddrHex, _, err := assetstypes.ParseID(record.StakerID) + if err != nil { + logger.Error( + "failed to parse staker ID", + "error", err, + ) continue } - } else { - err = k.assetsKeeper.UpdateStakerAssetState(ctx, record.StakerID, record.AssetID, assetstypes.DeltaStakerSingleAsset{ - WithdrawableAmount: record.ActualCompletedAmount, - PendingUndelegationAmount: recordAmountNeg, - }) + stakerAddrBytes, err := hexutil.Decode(stakerAddrHex) if err != nil { - k.Logger(oCtx).Error("failed to update staker asset state", "error", err) + logger.Error( + "failed to decode staker address", + "error", err, + ) + continue + } + stakerAddr := sdk.AccAddress(stakerAddrBytes) + if err := k.bankKeeper.UndelegateCoinsFromModuleToAccount( + ctx, types.DelegatedPoolName, stakerAddr, + sdk.NewCoins( + sdk.NewCoin(assetstypes.NativeAssetDenom, record.ActualCompletedAmount), + ), + ); err != nil { + logger.Error( + "failed to undelegate coins from module to account", + "error", err, + ) + continue + } + } else { + if err := k.assetsKeeper.UpdateStakerAssetState( + ctx, record.StakerID, record.AssetID, + assetstypes.DeltaStakerSingleAsset{ + WithdrawableAmount: record.ActualCompletedAmount, + PendingUndelegationAmount: recordAmountNeg, + }, + ); err != nil { + logger.Error( + "failed to update staker asset state", + "error", err, + ) continue } } // update the operator state - err = k.assetsKeeper.UpdateOperatorAssetState(ctx, operatorAccAddress, record.AssetID, assetstypes.DeltaOperatorSingleAsset{ - PendingUndelegationAmount: recordAmountNeg, - }) - if err != nil { - k.Logger(oCtx).Error("failed to update operator asset state", "error", err) + if err := k.assetsKeeper.UpdateOperatorAssetState( + ctx, operatorAccAddress, record.AssetID, + assetstypes.DeltaOperatorSingleAsset{ + PendingUndelegationAmount: recordAmountNeg, + }, + ); err != nil { + logger.Error( + "failed to update operator asset state", + "error", err, + ) continue } // delete the Undelegation records that have been complemented - // #nosec G703 // the error is always nil - _ = k.DeleteUndelegationRecord(ctx, record) + if err := k.DeleteUndelegationRecord(ctx, record); err != nil { + logger.Error( + "failed to delete undelegation record", + "error", err, + ) + continue + } // when calling `writeCache`, events are automatically emitted on the parent context writeCache() } diff --git a/x/delegation/keeper/msg_server.go b/x/delegation/keeper/msg_server.go index 735b58a3d..b538c928a 100644 --- a/x/delegation/keeper/msg_server.go +++ b/x/delegation/keeper/msg_server.go @@ -13,18 +13,16 @@ import ( var _ types.MsgServer = &Keeper{} -// DelegateAssetToOperator todo: Delegation and Undelegation from exoCore chain directly will be implemented in future.At the moment,they are executed from client chain -func (k *Keeper) DelegateAssetToOperator(goCtx context.Context, msg *types.MsgDelegation) (*types.DelegationResponse, error) { +// DelegateAssetToOperator delegates asset to operator. Currently, it only supports native token +func (k *Keeper) DelegateAssetToOperator( + goCtx context.Context, msg *types.MsgDelegation, +) (*types.DelegationResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) logger := k.Logger(ctx) - // TODO: currently we only support delegation with native token by invoking service - if msg.AssetID != assetstypes.NativeAssetID { - logger.Error("failed to delegate asset", "error", types.ErrNotSupportYet, "assetID", msg.AssetID) - return nil, types.ErrNotSupportYet.Wrap("assets other than native token are not supported yet") - } - logger.Info("DelegateAssetToOperator-nativeToken", "msg", msg) - + // no need to validate whether assetID == native token, since that is done by ValidateBasic. + // we can use `Must` since pre-validated fromAddr := sdk.MustAccAddressFromBech32(msg.BaseInfo.FromAddress) + // create nonce and unique hash nonce, err := k.accountKeeper.GetSequence(ctx, fromAddr) if err != nil { logger.Error("failed to get nonce", "error", err) @@ -35,30 +33,36 @@ func (k *Keeper) DelegateAssetToOperator(goCtx context.Context, msg *types.MsgDe combined := fmt.Sprintf("%s-%d", txHash, nonce) uniqueHash := sha256.Sum256([]byte(combined)) - // test for refactor - delegationParamsList := newDelegationParams(msg.BaseInfo, assetstypes.NativeAssetAddr, assetstypes.NativeChainLzID, nonce, uniqueHash) + delegationParamsList := newDelegationParams( + msg.BaseInfo, assetstypes.NativeAssetAddr, assetstypes.NativeChainLzID, + nonce, uniqueHash, + ) + cachedCtx, writeFunc := ctx.CacheContext() for _, delegationParams := range delegationParamsList { - if err := k.DelegateTo(ctx, delegationParams); err != nil { - logger.Error("failed to delegate asset", "error", err, "delegationParams", delegationParams) - return &types.DelegationResponse{}, err - + if err := k.DelegateTo(cachedCtx, delegationParams); err != nil { + logger.Error( + "failed to delegate asset", + "error", err, + "delegationParams", delegationParams, + ) + return nil, err } } - + writeFunc() return &types.DelegationResponse{}, nil } -func (k *Keeper) UndelegateAssetFromOperator(goCtx context.Context, msg *types.MsgUndelegation) (*types.UndelegationResponse, error) { +// UndelegateAssetFromOperator undelegates asset from operator. Currently, it only supports +// native token. +func (k *Keeper) UndelegateAssetFromOperator( + goCtx context.Context, msg *types.MsgUndelegation, +) (*types.UndelegationResponse, error) { ctx := sdk.UnwrapSDKContext(goCtx) logger := k.Logger(ctx) - // TODO: currently we only support undelegation with native token by invoking service - if msg.AssetID != assetstypes.NativeAssetID { - logger.Error("failed to undelegate asset", "error", types.ErrNotSupportYet, "assetID", msg.AssetID) - return nil, types.ErrNotSupportYet.Wrap("assets other than native token are not supported yet") - } - logger.Info("UndelegateAssetFromOperator", "msg", msg) - + // can use `Must` since pre-validated fromAddr := sdk.MustAccAddressFromBech32(msg.BaseInfo.FromAddress) + // no need to check that `assetID` is native token, since that is done by ValidateBasic. + // create nonce and unique hash nonce, err := k.accountKeeper.GetSequence(ctx, fromAddr) if err != nil { logger.Error("failed to get nonce", "error", err) @@ -69,19 +73,31 @@ func (k *Keeper) UndelegateAssetFromOperator(goCtx context.Context, msg *types.M combined := fmt.Sprintf("%s-%d", txHash, nonce) uniqueHash := sha256.Sum256([]byte(combined)) - inputParamsList := newDelegationParams(msg.BaseInfo, assetstypes.NativeAssetAddr, assetstypes.NativeChainLzID, nonce, uniqueHash) + inputParamsList := newDelegationParams( + msg.BaseInfo, assetstypes.NativeAssetAddr, assetstypes.NativeChainLzID, + nonce, uniqueHash, + ) + cachedCtx, writeFunc := ctx.CacheContext() for _, inputParams := range inputParamsList { - if err := k.UndelegateFrom(ctx, inputParams); err != nil { + if err := k.UndelegateFrom(cachedCtx, inputParams); err != nil { return nil, err } } + writeFunc() return &types.UndelegationResponse{}, nil } -func newDelegationParams(baseInfo *types.DelegationIncOrDecInfo, assetAddrStr string, clientChainLzID, txNonce uint64, txHash common.Hash) []*types.DelegationOrUndelegationParams { +// newDelegationParams creates delegation params from the given base info. +func newDelegationParams( + baseInfo *types.DelegationIncOrDecInfo, + assetAddrStr string, clientChainLzID uint64, txNonce uint64, + txHash common.Hash, +) []*types.DelegationOrUndelegationParams { + // can use `Must` since pre-validated stakerAddr := sdk.MustAccAddressFromBech32(baseInfo.FromAddress).Bytes() res := make([]*types.DelegationOrUndelegationParams, 0, 1) for _, kv := range baseInfo.PerOperatorAmounts { + // can use `Must` since pre-validated operatorAddr := sdk.MustAccAddressFromBech32(kv.Key) inputParams := types.NewDelegationOrUndelegationParams( clientChainLzID, diff --git a/x/delegation/keeper/un_delegation_state.go b/x/delegation/keeper/un_delegation_state.go index d0b1f72df..2f11bc568 100644 --- a/x/delegation/keeper/un_delegation_state.go +++ b/x/delegation/keeper/un_delegation_state.go @@ -13,6 +13,8 @@ import ( "github.com/ethereum/go-ethereum/common/hexutil" ) +// AllUndelegations function returns all the undelegation records in the module. +// It is used during `ExportGenesis` to export the undelegation records. func (k Keeper) AllUndelegations(ctx sdk.Context) (undelegations []types.UndelegationRecord, err error) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixUndelegationInfo) iterator := sdk.KVStorePrefixIterator(store, []byte{}) @@ -27,8 +29,13 @@ func (k Keeper) AllUndelegations(ctx sdk.Context) (undelegations []types.Undeleg return ret, nil } -// SetUndelegationRecords function saves the undelegation records to be handled when the handle time expires. -// When we save the undelegation records, we save them in three kv stores which are `KeyPrefixUndelegationInfo` `KeyPrefixStakerUndelegationInfo` and `KeyPrefixPendingUndelegations` +// SetUndelegationRecords stores the provided undelegation records. +// The records are stored with 3 different keys: +// (1) recordKey == blockNumber + lzNonce + txHash + operatorAddress => record +// (2) stakerID + assetID + lzNonce => recordKey +// (3) completeBlockNumber + lzNonce => recordKey +// If a record exists with the same key, it will be overwritten; however, that is not a big +// concern since the lzNonce and txHash are unique for each record. func (k *Keeper) SetUndelegationRecords(ctx sdk.Context, records []types.UndelegationRecord) error { singleRecordStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixUndelegationInfo) stakerUndelegationStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixStakerUndelegationInfo) @@ -53,6 +60,8 @@ func (k *Keeper) SetUndelegationRecords(ctx sdk.Context, records []types.Undeleg return nil } +// DeleteUndelegationRecord deletes the undelegation record from the module. +// The deletion is performed from all the 3 stores. func (k *Keeper) DeleteUndelegationRecord(ctx sdk.Context, record *types.UndelegationRecord) error { singleRecordStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixUndelegationInfo) stakerUndelegationStore := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixStakerUndelegationInfo) @@ -69,24 +78,7 @@ func (k *Keeper) DeleteUndelegationRecord(ctx sdk.Context, record *types.Undeleg return nil } -func (k *Keeper) SetSingleUndelegationRecord(ctx sdk.Context, record *types.UndelegationRecord) (recordKey []byte, err error) { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixUndelegationInfo) - bz := k.cdc.MustMarshal(record) - key := types.GetUndelegationRecordKey(record.BlockNumber, record.LzTxNonce, record.TxHash, record.OperatorAddr) - store.Set(key, bz) - return key, nil -} - -// StorePendingUndelegationRecord add it to handle the delay of completing undelegation caused by onHoldCount -// In the event that the undelegation is held by another module, this function is used within the EndBlocker to increment the scheduled completion block number by 1. -// Then the completion time of the undelegation will be delayed to the next block. -func (k *Keeper) StorePendingUndelegationRecord(ctx sdk.Context, singleRecKey []byte, record *types.UndelegationRecord) error { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixPendingUndelegations) - pendingUndelegationKey := types.GetPendingUndelegationRecordKey(record.CompleteBlockNumber, record.LzTxNonce) - store.Set(pendingUndelegationKey, singleRecKey) - return nil -} - +// GetUndelegationRecords returns the undelegation records for the provided record keys. func (k *Keeper) GetUndelegationRecords(ctx sdk.Context, singleRecordKeys []string) (record []*types.UndelegationRecord, err error) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixUndelegationInfo) ret := make([]*types.UndelegationRecord, 0) @@ -103,9 +95,9 @@ func (k *Keeper) GetUndelegationRecords(ctx sdk.Context, singleRecordKeys []stri return ret, nil } -// IterateUndelegationsByOperator iterate the undelegation records according to the operator -// and height filter. If the heightFilter isn't nil, only return the undelegations that the -// created height is greater than or equal to the filter height. +// IterateUndelegationsByOperator iterates over the undelegation records belonging to the +// provided operator and filter. If the filter is non-nil, it will only iterate over the +// records for which the block height is greater than or equal to the filter. func (k *Keeper) IterateUndelegationsByOperator( ctx sdk.Context, operator string, heightFilter *uint64, isUpdate bool, opFunc func(undelegation *types.UndelegationRecord) error, @@ -139,13 +131,8 @@ func (k *Keeper) IterateUndelegationsByOperator( return nil } -func (k *Keeper) SetStakerUndelegationInfo(ctx sdk.Context, stakerID, assetID string, recordKey []byte, lzNonce uint64) error { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixStakerUndelegationInfo) - key := types.GetStakerUndelegationRecordKey(stakerID, assetID, lzNonce) - store.Set(key, recordKey) - return nil -} - +// GetStakerUndelegationRecKeys returns the undelegation record keys corresponding to the provided +// staker and asset. func (k *Keeper) GetStakerUndelegationRecKeys(ctx sdk.Context, stakerID, assetID string) (recordKeyList []string, err error) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixStakerUndelegationInfo) iterator := sdk.KVStorePrefixIterator(store, []byte(strings.Join([]string{stakerID, assetID}, "/"))) @@ -158,6 +145,7 @@ func (k *Keeper) GetStakerUndelegationRecKeys(ctx sdk.Context, stakerID, assetID return ret, nil } +// GetStakerUndelegationRecords returns the undelegation records for the provided staker and asset. func (k *Keeper) GetStakerUndelegationRecords(ctx sdk.Context, stakerID, assetID string) (records []*types.UndelegationRecord, err error) { recordKeys, err := k.GetStakerUndelegationRecKeys(ctx, stakerID, assetID) if err != nil { @@ -167,13 +155,8 @@ func (k *Keeper) GetStakerUndelegationRecords(ctx sdk.Context, stakerID, assetID return k.GetUndelegationRecords(ctx, recordKeys) } -func (k *Keeper) SetPendingUndelegationInfo(ctx sdk.Context, height, lzNonce uint64, recordKey string) error { - store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixPendingUndelegations) - key := types.GetPendingUndelegationRecordKey(height, lzNonce) - store.Set(key, []byte(recordKey)) - return nil -} - +// GetPendingUndelegationRecKeys returns the undelegation record keys scheduled to mature at the +// end of the block with the provided height. func (k *Keeper) GetPendingUndelegationRecKeys(ctx sdk.Context, height uint64) (recordKeyList []string, err error) { store := prefix.NewStore(ctx.KVStore(k.storeKey), types.KeyPrefixPendingUndelegations) iterator := sdk.KVStorePrefixIterator(store, []byte(hexutil.EncodeUint64(height))) @@ -186,6 +169,8 @@ func (k *Keeper) GetPendingUndelegationRecKeys(ctx sdk.Context, height uint64) ( return ret, nil } +// GetPendingUndelegationRecords returns the undelegation records scheduled to mature at the end +// of the block with the provided height. func (k *Keeper) GetPendingUndelegationRecords(ctx sdk.Context, height uint64) (records []*types.UndelegationRecord, err error) { recordKeys, err := k.GetPendingUndelegationRecKeys(ctx, height) if err != nil { @@ -199,6 +184,7 @@ func (k *Keeper) GetPendingUndelegationRecords(ctx sdk.Context, height uint64) ( return k.GetUndelegationRecords(ctx, recordKeys) } +// IncrementUndelegationHoldCount increments the hold count for the undelegation record key. func (k Keeper) IncrementUndelegationHoldCount(ctx sdk.Context, recordKey []byte) error { prev := k.GetUndelegationHoldCount(ctx, recordKey) if prev == math.MaxUint64 { @@ -210,12 +196,14 @@ func (k Keeper) IncrementUndelegationHoldCount(ctx sdk.Context, recordKey []byte return nil } +// GetUndelegationHoldCount returns the hold count for the undelegation record key. func (k *Keeper) GetUndelegationHoldCount(ctx sdk.Context, recordKey []byte) uint64 { store := ctx.KVStore(k.storeKey) bz := store.Get(types.GetUndelegationOnHoldKey(recordKey)) return sdk.BigEndianToUint64(bz) } +// DecrementUndelegationHoldCount decrements the hold count for the undelegation record key. func (k Keeper) DecrementUndelegationHoldCount(ctx sdk.Context, recordKey []byte) error { prev := k.GetUndelegationHoldCount(ctx, recordKey) if prev == 0 { diff --git a/x/delegation/types/msg.go b/x/delegation/types/msg.go index 1a2e22fa2..8ed203b82 100644 --- a/x/delegation/types/msg.go +++ b/x/delegation/types/msg.go @@ -23,13 +23,18 @@ func (m *MsgDelegation) ValidateBasic() error { } // new message to delegate asset to operator -func NewMsgDelegation(assetID, fromAddress string, amountPerOperator []KeyValue) *MsgDelegation { +func NewMsgDelegation( + assetID string, fromAddress string, amountPerOperator []KeyValue, +) *MsgDelegation { baseInfo := &DelegationIncOrDecInfo{ FromAddress: fromAddress, PerOperatorAmounts: make([]KeyValue, 0, 1), } for _, kv := range amountPerOperator { - baseInfo.PerOperatorAmounts = append(baseInfo.PerOperatorAmounts, KeyValue{Key: kv.Key, Value: kv.Value}) + baseInfo.PerOperatorAmounts = append( + baseInfo.PerOperatorAmounts, + KeyValue{Key: kv.Key, Value: kv.Value}, + ) } return &MsgDelegation{ AssetID: assetID, @@ -65,7 +70,10 @@ func NewMsgUndelegation(assetID, fromAddress string, amountPerOperator []KeyValu PerOperatorAmounts: make([]KeyValue, 0, 1), } for _, kv := range amountPerOperator { - baseInfo.PerOperatorAmounts = append(baseInfo.PerOperatorAmounts, KeyValue{Key: kv.Key, Value: kv.Value}) + baseInfo.PerOperatorAmounts = append( + baseInfo.PerOperatorAmounts, + KeyValue{Key: kv.Key, Value: kv.Value}, + ) } return &MsgUndelegation{ AssetID: assetID, @@ -73,18 +81,28 @@ func NewMsgUndelegation(assetID, fromAddress string, amountPerOperator []KeyValu } } -// TODO: delegation and undelegation have the same params, try to use one single message with different flag to indicate action:delegation/undelegation +// validateDelegationInfo validates the delegation or undelegation info. +// (1) the operator amounts are positive, and the operator addresses are valid. +// (2) the assetID is native only, since only native token is supported for this mechanism. +// (3) the from address is valid. +// TODO: delegation and undelegation have the same params, try to use one single message with +// different flag to indicate action:delegation/undelegation func validateDelegationInfo(assetID string, baseInfo *DelegationIncOrDecInfo) error { for _, kv := range baseInfo.PerOperatorAmounts { if _, err := sdk.AccAddressFromBech32(kv.Key); err != nil { return errorsmod.Wrap(err, "invalid operator address delegateTO") } if !kv.Value.Amount.IsPositive() { - return errorsmod.Wrapf(ErrAmountIsNotPositive, "amount should be positive, got%s", kv.Value.Amount.String()) + return ErrAmountIsNotPositive.Wrapf( + "amount should be positive, got %s", kv.Value.Amount.String(), + ) } } if assetID != assetstype.NativeAssetID { - return errorsmod.Wrapf(ErrInvalidAssetID, "only nativeToken is support, expected:%s,got:%s", assetstype.NativeAssetID, assetID) + return ErrInvalidAssetID.Wrapf( + "only nativeToken is support, expected:%s, got:%s", + assetstype.NativeAssetID, assetID, + ) } if _, err := sdk.AccAddressFromBech32(baseInfo.FromAddress); err != nil { return errorsmod.Wrap(err, "invalid from address") From 3baafe94a590ffeacb5e9e7e1bf29c0ea0b76cd2 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Fri, 11 Oct 2024 02:58:12 +0000 Subject: [PATCH 19/24] fix(ci): upgrade Docker curl version --- Dockerfile | 2 +- networks/local/exocore/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index df79f9de7..469f75ab6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,7 +23,7 @@ WORKDIR /root COPY --from=build-env /go/src/github.com/ExocoreNetwork/exocore/build/exocored /usr/bin/exocored COPY --from=build-env /go/bin/toml-cli /usr/bin/toml-cli -RUN apk add --no-cache ca-certificates=20240226-r0 libstdc++=13.2.1_git20231014-r0 jq=1.7.1-r0 curl=8.9.1-r0 bash=5.2.21-r0 vim=9.0.2127-r0 lz4=1.9.4-r5 rclone=1.65.0-r3 \ +RUN apk add --no-cache ca-certificates=20240226-r0 libstdc++=13.2.1_git20231014-r0 jq=1.7.1-r0 curl=8.9.1-r1 bash=5.2.21-r0 vim=9.0.2127-r0 lz4=1.9.4-r5 rclone=1.65.0-r3 \ && addgroup -g 1000 exocore \ && adduser -S -h /home/exocore -D exocore -u 1000 -G exocore diff --git a/networks/local/exocore/Dockerfile b/networks/local/exocore/Dockerfile index 1b36ccc8f..404e8b6d6 100644 --- a/networks/local/exocore/Dockerfile +++ b/networks/local/exocore/Dockerfile @@ -10,7 +10,7 @@ RUN LEDGER_ENABLED=false make build ##################################### FROM alpine:3.19 AS run -RUN apk add --no-cache libstdc++=13.2.1_git20231014-r0 bash=5.2.21-r0 curl=8.9.1-r0 jq=1.7.1-r0 \ +RUN apk add --no-cache libstdc++=13.2.1_git20231014-r0 bash=5.2.21-r0 curl=8.9.1-r1 jq=1.7.1-r0 \ && addgroup -g 1000 exocore \ && adduser -S -h /home/exocore -D exocore -u 1000 -G exocore EXPOSE 26656 26657 1317 9090 8545 8546 From f9338bfdbe279c4c5a6a4cb6f02fd94502953864 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Fri, 11 Oct 2024 03:38:11 +0000 Subject: [PATCH 20/24] fix: error duplicate asset registration --- precompiles/assets/tx.go | 13 ++--------- x/assets/keeper/client_chain_asset.go | 32 ++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 16 deletions(-) diff --git a/precompiles/assets/tx.go b/precompiles/assets/tx.go index 4a220fb57..fd5073ac8 100644 --- a/precompiles/assets/tx.go +++ b/precompiles/assets/tx.go @@ -189,18 +189,9 @@ func (p Precompile) UpdateToken( return nil, err } - // check that the asset being updated actually exists _, assetID := assetstypes.GetStakerIDAndAssetIDFromStr(uint64(clientChainID), "", hexAssetAddr) - assetInfo, err := p.assetsKeeper.GetStakingAssetInfo(ctx, assetID) - if err != nil { - // fails if asset does not exist with ErrNoClientChainAssetKey - return nil, err - } - - // finally, execute the update - assetInfo.AssetBasicInfo.MetaInfo = metadata - - if err := p.assetsKeeper.SetStakingAssetInfo(ctx, assetInfo); err != nil { + // this verifies the existence of the asset and returns an error if it doesn't exist + if err := p.assetsKeeper.UpdateStakingAssetMetaInfo(ctx, assetID, metadata); err != nil { return nil, err } diff --git a/x/assets/keeper/client_chain_asset.go b/x/assets/keeper/client_chain_asset.go index 5fd3cb924..4aa9c1fcf 100644 --- a/x/assets/keeper/client_chain_asset.go +++ b/x/assets/keeper/client_chain_asset.go @@ -35,8 +35,9 @@ func (k Keeper) UpdateStakingAssetTotalAmount(ctx sdk.Context, assetID string, c } // SetStakingAssetInfo todo: Temporarily use clientChainAssetAddr+'_'+LayerZeroChainID as the key. -// It provides a function to register the client chain assets supported by exoCore.It's called by genesis configuration now,however it will be called by the governance in the future -// The caller is responsible for ensuring that no such asset already exists (if a new asset is being created) +// It provides a function to register the client chain assets supported by Exocore. +// New assets may be registered via ExocoreGateway, which uses precompiles to call this function. +// If an asset with the provided assetID already exists, it will return an error. func (k Keeper) SetStakingAssetInfo(ctx sdk.Context, info *assetstype.StakingAssetInfo) (err error) { if info.AssetBasicInfo.Decimals > assetstype.MaxDecimal { return errorsmod.Wrapf(assetstype.ErrInvalidInputParameter, "the decimal is greater than the MaxDecimal,decimal:%v,MaxDecimal:%v", info.AssetBasicInfo.Decimals, assetstype.MaxDecimal) @@ -45,19 +46,39 @@ func (k Keeper) SetStakingAssetInfo(ctx sdk.Context, info *assetstype.StakingAss return errorsmod.Wrapf(assetstype.ErrInvalidInputParameter, "the total staking amount is negative, StakingTotalAmount:%v", info.StakingTotalAmount) } store := prefix.NewStore(ctx.KVStore(k.storeKey), assetstype.KeyPrefixReStakingAssetInfo) - // key := common.HexToAddress(incentive.Contract) - bz := k.cdc.MustMarshal(info) - _, assetID := assetstype.GetStakerIDAndAssetIDFromStr(info.AssetBasicInfo.LayerZeroChainID, "", info.AssetBasicInfo.Address) + if k.IsStakingAsset(ctx, assetID) { + return assetstype.ErrInvalidInputParameter.Wrapf( + "the asset has already been registered,assetID:%v,LayerZeroChainID:%v,ClientChainAssetAddr:%v", + assetID, info.AssetBasicInfo.LayerZeroChainID, info.AssetBasicInfo.Address, + ) + } + bz := k.cdc.MustMarshal(info) store.Set([]byte(assetID), bz) return nil } +// IsStakingAsset checks if the assetID is a staking asset. func (k Keeper) IsStakingAsset(ctx sdk.Context, assetID string) bool { store := prefix.NewStore(ctx.KVStore(k.storeKey), assetstype.KeyPrefixReStakingAssetInfo) return store.Has([]byte(assetID)) } +// UpdateStakingAssetMetaInfo updates the meta information stored against a provided assetID. +// If the assetID does not exist, it returns an error. +func (k Keeper) UpdateStakingAssetMetaInfo(ctx sdk.Context, assetID string, metainfo string) error { + info, err := k.GetStakingAssetInfo(ctx, assetID) + if err != nil { + return err + } + store := prefix.NewStore(ctx.KVStore(k.storeKey), assetstype.KeyPrefixReStakingAssetInfo) + info.AssetBasicInfo.MetaInfo = metainfo + bz := k.cdc.MustMarshal(info) + store.Set([]byte(assetID), bz) + return nil +} + +// GetStakingAssetInfo returns the asset information stored against a provided assetID. func (k Keeper) GetStakingAssetInfo(ctx sdk.Context, assetID string) (info *assetstype.StakingAssetInfo, err error) { store := prefix.NewStore(ctx.KVStore(k.storeKey), assetstype.KeyPrefixReStakingAssetInfo) value := store.Get([]byte(assetID)) @@ -70,6 +91,7 @@ func (k Keeper) GetStakingAssetInfo(ctx sdk.Context, assetID string) (info *asse return &ret, nil } +// GetAssetsDecimal returns the decimal of all the provided assets. func (k Keeper) GetAssetsDecimal(ctx sdk.Context, assets map[string]interface{}) (decimals map[string]uint32, err error) { if assets == nil { return nil, errorsmod.Wrap(assetstype.ErrInputPointerIsNil, "assets is nil") From 98f95df96ad5678da1780d6088ecd7fe781c4703 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Fri, 11 Oct 2024 03:50:36 +0000 Subject: [PATCH 21/24] fix: optimize store open + add TODO --- x/assets/keeper/client_chain_asset.go | 2 +- x/avs/types/keys.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/x/assets/keeper/client_chain_asset.go b/x/assets/keeper/client_chain_asset.go index 4aa9c1fcf..ef7b99f43 100644 --- a/x/assets/keeper/client_chain_asset.go +++ b/x/assets/keeper/client_chain_asset.go @@ -47,7 +47,7 @@ func (k Keeper) SetStakingAssetInfo(ctx sdk.Context, info *assetstype.StakingAss } store := prefix.NewStore(ctx.KVStore(k.storeKey), assetstype.KeyPrefixReStakingAssetInfo) _, assetID := assetstype.GetStakerIDAndAssetIDFromStr(info.AssetBasicInfo.LayerZeroChainID, "", info.AssetBasicInfo.Address) - if k.IsStakingAsset(ctx, assetID) { + if store.Has([]byte(assetID)) { return assetstype.ErrInvalidInputParameter.Wrapf( "the asset has already been registered,assetID:%v,LayerZeroChainID:%v,ClientChainAssetAddr:%v", assetID, info.AssetBasicInfo.LayerZeroChainID, info.AssetBasicInfo.Address, diff --git a/x/avs/types/keys.go b/x/avs/types/keys.go index c25d14390..289a43667 100644 --- a/x/avs/types/keys.go +++ b/x/avs/types/keys.go @@ -23,7 +23,7 @@ const ( const ( // prefixAVSInfo is the prefix for the AVS info store. It starts with a value of 5 // for backward compatibility with the previous prefix used for the AVS info store. - // at the time of a chain-id upgrade, this may be reset to 1. + // TODO at the time of a chain-id upgrade, this may be reset to 1. prefixAVSInfo = iota + 5 prefixAVSOperatorInfo prefixParams From d0c1b627ed84cf5b9fbee91da63030ac5eeb9121 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Fri, 11 Oct 2024 04:46:15 +0000 Subject: [PATCH 22/24] refactor: respond to AI comments --- precompiles/assets/IAssets.sol | 6 ++---- x/assets/keeper/client_chain_asset.go | 2 +- x/assets/types/errors.go | 6 +++++- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/precompiles/assets/IAssets.sol b/precompiles/assets/IAssets.sol index 31a82e329..cd75e86ca 100644 --- a/precompiles/assets/IAssets.sol +++ b/precompiles/assets/IAssets.sol @@ -14,10 +14,8 @@ IAssets constant ASSETS_CONTRACT = IAssets(ASSETS_PRECOMPILE_ADDRESS); interface IAssets { /// TRANSACTIONS - /// @dev deposit the client chain assets for the staker, + /// @dev deposit the client chain assets, only LSTs, for the staker, /// that will change the state in assets module - /// @dev deposit the client chain assets, mainly LSTs, for the staker, - /// that will change the state in deposit module /// Note that this address cannot be a module account. /// @param clientChainID is the layerZero chainID if it is supported. // It might be allocated by Exocore when the client chain isn't supported @@ -63,7 +61,7 @@ interface IAssets { uint256 opAmount ) external returns (bool success, uint256 latestAssetState); - /// @dev withdraw NST To the staker, that will change the state in withdraw module + /// @dev withdraw NST To the staker, that will change the state in assets module /// Note that this address cannot be a module account. /// @param clientChainID is the layerZero chainID if it is supported. // It might be allocated by Exocore when the client chain isn't supported diff --git a/x/assets/keeper/client_chain_asset.go b/x/assets/keeper/client_chain_asset.go index ef7b99f43..06a861cbb 100644 --- a/x/assets/keeper/client_chain_asset.go +++ b/x/assets/keeper/client_chain_asset.go @@ -48,7 +48,7 @@ func (k Keeper) SetStakingAssetInfo(ctx sdk.Context, info *assetstype.StakingAss store := prefix.NewStore(ctx.KVStore(k.storeKey), assetstype.KeyPrefixReStakingAssetInfo) _, assetID := assetstype.GetStakerIDAndAssetIDFromStr(info.AssetBasicInfo.LayerZeroChainID, "", info.AssetBasicInfo.Address) if store.Has([]byte(assetID)) { - return assetstype.ErrInvalidInputParameter.Wrapf( + return assetstype.ErrRegisterDuplicateAssetID.Wrapf( "the asset has already been registered,assetID:%v,LayerZeroChainID:%v,ClientChainAssetAddr:%v", assetID, info.AssetBasicInfo.LayerZeroChainID, info.AssetBasicInfo.Address, ) diff --git a/x/assets/types/errors.go b/x/assets/types/errors.go index 38bf37ebb..8cfc47fe0 100644 --- a/x/assets/types/errors.go +++ b/x/assets/types/errors.go @@ -89,8 +89,12 @@ var ( ModuleName, 18, "the operation type is invalid") - ErrParseJoinedKey = errorsmod.Register( + ErrRegisterDuplicateAssetID = errorsmod.Register( ModuleName, 19, + "register new asset with an existing assetID") + + ErrParseJoinedKey = errorsmod.Register( + ModuleName, 20, "the joined key can't be parsed", ) ) From 81c7a8c1c8dd4213376f11e1c74b7c6b65e7be5d Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Fri, 11 Oct 2024 05:16:40 +0000 Subject: [PATCH 23/24] doc: remove superfluous comment --- x/delegation/keeper/abci.go | 1 - 1 file changed, 1 deletion(-) diff --git a/x/delegation/keeper/abci.go b/x/delegation/keeper/abci.go index 37d2af7b5..8a44e7496 100644 --- a/x/delegation/keeper/abci.go +++ b/x/delegation/keeper/abci.go @@ -61,7 +61,6 @@ func (k *Keeper) EndBlock( if _, err := k.UpdateDelegationState( ctx, record.StakerID, record.AssetID, record.OperatorAddr, deltaAmount, ); err != nil { - // use oCtx so that the error is logged on the original context logger.Error( "failed to update delegation state", "error", err, From 43e637e2bdef1c93f5a4f58925ef6c784ab3d430 Mon Sep 17 00:00:00 2001 From: MaxMustermann2 <82761650+MaxMustermann2@users.noreply.github.com> Date: Fri, 11 Oct 2024 05:18:49 +0000 Subject: [PATCH 24/24] doc: add comment on evm --- x/evm/keeper/state_transition.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/x/evm/keeper/state_transition.go b/x/evm/keeper/state_transition.go index 8621155a3..b78b1ce83 100644 --- a/x/evm/keeper/state_transition.go +++ b/x/evm/keeper/state_transition.go @@ -225,6 +225,8 @@ func (k *Keeper) ApplyTransaction(ctx sdk.Context, tx *ethtypes.Transaction) (*t commit() // Since the post-processing can alter the log, we need to update the result res.Logs = types.NewLogsFromEth(receipt.Logs) + // no need to re-emit the events on ctx; see + // https://github.com/evmos/evmos/pull/2559 } }