feat: module circuit breaker (#14521)

Co-authored-by: Aaron Craelius <aaron@regen.network>
Co-authored-by: Julien Robert <julien@rbrt.fr>
Co-authored-by: Sam Ricotta <samanthalricotta@gmail.com>
Co-authored-by: samricotta <37125168+samricotta@users.noreply.github.com>
Co-authored-by: Facundo Medica <14063057+facundomedica@users.noreply.github.com>
This commit is contained in:
Marko 2023-05-15 16:38:48 +02:00 committed by GitHub
parent d818a628a1
commit b8e15a7930
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 2746 additions and 543 deletions

View File

@ -3,7 +3,6 @@ package circuitv1
import (
v1beta1 "cosmossdk.io/api/cosmos/base/query/v1beta1"
_ "cosmossdk.io/api/cosmos/msg/v1"
_ "cosmossdk.io/api/cosmos/query/v1"
fmt "fmt"
runtime "github.com/cosmos/cosmos-proto/runtime"
@ -2953,72 +2952,70 @@ var file_cosmos_circuit_v1_query_proto_rawDesc = []byte{
0x11, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e,
0x76, 0x31, 0x1a, 0x2a, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x62, 0x61, 0x73, 0x65, 0x2f,
0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2f, 0x70, 0x61,
0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x17,
0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x6d, 0x73, 0x67, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x73,
0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f,
0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73,
0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61,
0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x71, 0x75, 0x65,
0x72, 0x79, 0x2f, 0x76, 0x31, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x22, 0x2f, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72,
0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x22, 0x51, 0x0a, 0x0f, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73,
0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x73, 0x6d,
0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65,
0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69,
0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5e, 0x0a, 0x14, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a,
0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28,
0x0b, 0x32, 0x26, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e,
0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61,
0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e,
0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa5, 0x01, 0x0a, 0x10, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x48, 0x0a, 0x08, 0x61, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31,
0x2e, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50,
0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x73, 0x12, 0x47, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f,
0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d,
0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2f, 0x76,
0x31, 0x2f, 0x74, 0x79, 0x70, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1c, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1b, 0x63, 0x6f, 0x73,
0x6d, 0x6f, 0x73, 0x2f, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2f, 0x76, 0x31, 0x2f, 0x71, 0x75, 0x65,
0x72, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2f, 0x0a, 0x13, 0x51, 0x75, 0x65, 0x72,
0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x22, 0x51, 0x0a, 0x0f, 0x41, 0x63, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x3e, 0x0a, 0x0a,
0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x32, 0x1e, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69,
0x74, 0x2e, 0x76, 0x31, 0x2e, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x73,
0x52, 0x0a, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x22, 0x5e, 0x0a, 0x14,
0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x12, 0x46, 0x0a, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69,
0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f,
0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x71, 0x75, 0x65, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x62,
0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a,
0x18, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69,
0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x3b, 0x0a, 0x14, 0x44, 0x69, 0x73,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6c, 0x69,
0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x32, 0xb4, 0x03, 0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79,
0x12, 0x89, 0x01, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x12, 0x26, 0x2e, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31,
0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69,
0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x32, 0x88, 0xe7, 0xb0, 0x2a, 0x01, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x27, 0x12, 0x25, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63,
0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x7d, 0x12, 0x82, 0x01, 0x0a,
0x08, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x27, 0x2e, 0x63, 0x6f, 0x73, 0x6d,
0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0xa5, 0x01, 0x0a,
0x10, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
0x65, 0x12, 0x48, 0x0a, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x18, 0x01, 0x20,
0x03, 0x28, 0x0b, 0x32, 0x2c, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72,
0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x6e, 0x65, 0x73, 0x69, 0x73, 0x41,
0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x50, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x73, 0x52, 0x08, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x47, 0x0a, 0x0a, 0x70,
0x61, 0x67, 0x69, 0x6e, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x27, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x62, 0x61, 0x73, 0x65, 0x2e, 0x71, 0x75,
0x65, 0x72, 0x79, 0x2e, 0x76, 0x31, 0x62, 0x65, 0x74, 0x61, 0x31, 0x2e, 0x50, 0x61, 0x67, 0x65,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x52, 0x0a, 0x70, 0x61, 0x67, 0x69, 0x6e, 0x61,
0x74, 0x69, 0x6f, 0x6e, 0x22, 0x1a, 0x0a, 0x18, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x69, 0x73,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x22, 0x3b, 0x0a, 0x14, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x64, 0x69, 0x73, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52,
0x0c, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x32, 0xad, 0x03,
0x0a, 0x05, 0x51, 0x75, 0x65, 0x72, 0x79, 0x12, 0x89, 0x01, 0x0a, 0x07, 0x41, 0x63, 0x63, 0x6f,
0x75, 0x6e, 0x74, 0x12, 0x26, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72,
0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x63, 0x63,
0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x63, 0x6f,
0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e,
0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x32, 0x88, 0xe7, 0xb0, 0x2a, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x27, 0x12, 0x25, 0x2f, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2f, 0x76, 0x31,
0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x2f, 0x7b, 0x61, 0x64, 0x64, 0x72, 0x65,
0x73, 0x73, 0x7d, 0x12, 0x82, 0x01, 0x0a, 0x08, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73,
0x12, 0x27, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69,
0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e,
0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x73, 0x6d,
0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63,
0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28,
0x88, 0xe7, 0xb0, 0x2a, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x63, 0x6f,
0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2f, 0x76, 0x31, 0x2f,
0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x12, 0x92, 0x01, 0x0a, 0x0c, 0x44, 0x69, 0x73,
0x61, 0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x73, 0x6d,
0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75,
0x65, 0x72, 0x79, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65,
0x73, 0x74, 0x1a, 0x23, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63,
0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x73, 0x52,
0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x28, 0x88, 0xe7, 0xb0, 0x2a, 0x01, 0x82, 0xd3,
0xe4, 0x93, 0x02, 0x1d, 0x12, 0x1b, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x69,
0x72, 0x63, 0x75, 0x69, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x63, 0x63, 0x6f, 0x75, 0x6e, 0x74,
0x73, 0x12, 0x92, 0x01, 0x0a, 0x0c, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69,
0x73, 0x74, 0x12, 0x2b, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63,
0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x51, 0x75, 0x65, 0x72, 0x79, 0x44, 0x69, 0x73, 0x61,
0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a,
0x27, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74,
0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74,
0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2c, 0x88, 0xe7, 0xb0, 0x2a, 0x01, 0x82,
0xd3, 0xe4, 0x93, 0x02, 0x21, 0x12, 0x1f, 0x2f, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63,
0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2f, 0x76, 0x31, 0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c,
0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x1a, 0x05, 0x80, 0xe7, 0xb0, 0x2a, 0x01, 0x42, 0xb7, 0x01,
0x65, 0x72, 0x79, 0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x27, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e,
0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x44, 0x69, 0x73, 0x61, 0x62,
0x6c, 0x65, 0x64, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
0x2c, 0x88, 0xe7, 0xb0, 0x2a, 0x01, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x21, 0x12, 0x1f, 0x2f, 0x63,
0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2f, 0x63, 0x69, 0x72, 0x63, 0x75, 0x69, 0x74, 0x2f, 0x76, 0x31,
0x2f, 0x64, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x6c, 0x69, 0x73, 0x74, 0x42, 0xb7, 0x01,
0x0a, 0x15, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x2e, 0x63, 0x69, 0x72,
0x63, 0x75, 0x69, 0x74, 0x2e, 0x76, 0x31, 0x42, 0x0a, 0x51, 0x75, 0x65, 0x72, 0x79, 0x50, 0x72,
0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x63, 0x6f, 0x73, 0x6d, 0x6f, 0x73, 0x73, 0x64, 0x6b,

View File

@ -431,6 +431,12 @@ func (app *BaseApp) setState(mode runTxMode, header cmtproto.Header) {
}
}
// SetCircuitBreaker sets the circuit breaker for the BaseApp.
// The circuit breaker is checked on every message execution to verify if a transaction should be executed or not.
func (app *BaseApp) SetCircuitBreaker(cb CircuitBreaker) {
app.msgServiceRouter.SetCircuit(cb)
}
// GetConsensusParams returns the current consensus parameters from the BaseApp's
// ParamStore. If the BaseApp has no ParamStore defined, nil is returned.
func (app *BaseApp) GetConsensusParams(ctx sdk.Context) cmtproto.ConsensusParams {

10
baseapp/circuit.go Normal file
View File

@ -0,0 +1,10 @@
package baseapp
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
// CircuitBreaker is an interface that defines the methods for a circuit breaker.
type CircuitBreaker interface {
IsAllowed(ctx sdk.Context, typeURL string) bool
}

View File

@ -26,6 +26,7 @@ type MessageRouter interface {
type MsgServiceRouter struct {
interfaceRegistry codectypes.InterfaceRegistry
routes map[string]MsgServiceHandler
circuitBreaker CircuitBreaker
}
var _ gogogrpc.Server = &MsgServiceRouter{}
@ -37,6 +38,10 @@ func NewMsgServiceRouter() *MsgServiceRouter {
}
}
func (msr *MsgServiceRouter) SetCircuit(cb CircuitBreaker) {
msr.circuitBreaker = cb
}
// MsgServiceHandler defines a function type which handles Msg service message.
type MsgServiceHandler = func(ctx sdk.Context, req sdk.Msg) (*sdk.Result, error)
@ -128,6 +133,13 @@ func (msr *MsgServiceRouter) RegisterService(sd *grpc.ServiceDesc, handler inter
}
}
if msr.circuitBreaker != nil {
msgURL := sdk.MsgTypeURL(msg)
if !msr.circuitBreaker.IsAllowed(ctx, msgURL) {
return nil, fmt.Errorf("circuit breaker disables execution of this message: %s", msgURL)
}
}
// Call the method handler from the service description with the handler object.
// We don't do any decoding here because the decoding was already done.
res, err := methodHandler(handler, ctx, noopDecoder, interceptor)

View File

@ -4,15 +4,12 @@ package cosmos.circuit.v1;
option go_package = "cosmossdk.io/x/circuit/types";
import "cosmos/base/query/v1beta1/pagination.proto";
import "cosmos/msg/v1/msg.proto";
import "cosmos/circuit/v1/types.proto";
import "google/api/annotations.proto";
import "cosmos/query/v1/query.proto";
// Msg defines the crisis Msg service.
service Query {
option (cosmos.msg.v1.service) = true;
// Account returns account permissions.
rpc Account(QueryAccountRequest) returns (AccountResponse) {
option (cosmos.query.v1.module_query_safe) = true;

50
simapp/ante.go Normal file
View File

@ -0,0 +1,50 @@
package simapp
import (
"errors"
circuitante "cosmossdk.io/x/circuit/ante"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/x/auth/ante"
)
// HandlerOptions are the options required for constructing a default SDK AnteHandler.
type HandlerOptions struct {
ante.HandlerOptions
CircuitKeeper circuitante.CircuitBreaker
}
// NewAnteHandler returns an AnteHandler that checks and increments sequence
// numbers, checks signatures & account numbers, and deducts fees from the first
// signer.
func NewAnteHandler(options HandlerOptions) (sdk.AnteHandler, error) {
if options.AccountKeeper == nil {
return nil, errors.New("account keeper is required for ante builder")
}
if options.BankKeeper == nil {
return nil, errors.New("bank keeper is required for ante builder")
}
if options.SignModeHandler == nil {
return nil, errors.New("sign mode handler is required for ante builder")
}
anteDecorators := []sdk.AnteDecorator{
ante.NewSetUpContextDecorator(), // outermost AnteDecorator. SetUpContext must be called first
circuitante.NewCircuitBreakerDecorator(options.CircuitKeeper),
ante.NewExtensionOptionsDecorator(options.ExtensionOptionChecker),
ante.NewValidateBasicDecorator(),
ante.NewTxTimeoutHeightDecorator(),
ante.NewValidateMemoDecorator(options.AccountKeeper),
ante.NewConsumeGasForTxSizeDecorator(options.AccountKeeper),
ante.NewDeductFeeDecorator(options.AccountKeeper, options.BankKeeper, options.FeegrantKeeper, options.TxFeeChecker),
ante.NewSetPubKeyDecorator(options.AccountKeeper), // SetPubKeyDecorator must be called before all signature verification decorators
ante.NewValidateSigCountDecorator(options.AccountKeeper),
ante.NewSigGasConsumeDecorator(options.AccountKeeper, options.SigGasConsumer),
ante.NewSigVerificationDecorator(options.AccountKeeper, options.SignModeHandler),
ante.NewIncrementSequenceDecorator(options.AccountKeeper),
}
return sdk.ChainAnteDecorators(anteDecorators...), nil
}

View File

@ -36,6 +36,9 @@ import (
upgradekeeper "cosmossdk.io/x/upgrade/keeper"
upgradetypes "cosmossdk.io/x/upgrade/types"
"cosmossdk.io/x/circuit"
circuitkeeper "cosmossdk.io/x/circuit/keeper"
circuittypes "cosmossdk.io/x/circuit/types"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
@ -159,6 +162,7 @@ type SimApp struct {
GroupKeeper groupkeeper.Keeper
NFTKeeper nftkeeper.Keeper
ConsensusParamsKeeper consensusparamkeeper.Keeper
CircuitKeeper circuitkeeper.Keeper
// the module manager
ModuleManager *module.Manager
@ -235,7 +239,8 @@ func NewSimApp(
authtypes.StoreKey, banktypes.StoreKey, stakingtypes.StoreKey, crisistypes.StoreKey,
minttypes.StoreKey, distrtypes.StoreKey, slashingtypes.StoreKey,
govtypes.StoreKey, paramstypes.StoreKey, consensusparamtypes.StoreKey, upgradetypes.StoreKey, feegrant.StoreKey,
evidencetypes.StoreKey, authzkeeper.StoreKey, nftkeeper.StoreKey, group.StoreKey,
evidencetypes.StoreKey, circuittypes.StoreKey,
authzkeeper.StoreKey, nftkeeper.StoreKey, group.StoreKey,
)
// register streaming services
@ -294,6 +299,8 @@ func NewSimApp(
stakingtypes.NewMultiStakingHooks(app.DistrKeeper.Hooks(), app.SlashingKeeper.Hooks()),
)
app.CircuitKeeper = circuitkeeper.NewKeeper(keys[circuittypes.StoreKey], authtypes.NewModuleAddress(govtypes.ModuleName).String(), app.AccountKeeper.GetAddressCodec())
app.AuthzKeeper = authzkeeper.NewKeeper(runtime.NewKVStoreService(keys[authzkeeper.StoreKey]), appCodec, app.MsgServiceRouter(), app.AccountKeeper)
groupConfig := group.DefaultConfig()
@ -378,6 +385,7 @@ func NewSimApp(
groupmodule.NewAppModule(appCodec, app.GroupKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
nftmodule.NewAppModule(appCodec, app.NFTKeeper, app.AccountKeeper, app.BankKeeper, app.interfaceRegistry),
consensus.NewAppModule(appCodec, app.ConsensusParamsKeeper),
circuit.NewAppModule(appCodec, app.CircuitKeeper),
)
// BasicModuleManager defines the module BasicManager is in charge of setting up basic,
@ -428,7 +436,7 @@ func NewSimApp(
distrtypes.ModuleName, stakingtypes.ModuleName, slashingtypes.ModuleName, govtypes.ModuleName,
minttypes.ModuleName, crisistypes.ModuleName, genutiltypes.ModuleName, evidencetypes.ModuleName, authz.ModuleName,
feegrant.ModuleName, nft.ModuleName, group.ModuleName, paramstypes.ModuleName, upgradetypes.ModuleName,
vestingtypes.ModuleName, consensusparamtypes.ModuleName,
vestingtypes.ModuleName, consensusparamtypes.ModuleName, circuittypes.ModuleName,
}
app.ModuleManager.SetOrderInitGenesis(genesisModuleOrder...)
app.ModuleManager.SetOrderExportGenesis(genesisModuleOrder...)
@ -522,19 +530,23 @@ func NewSimApp(
}
func (app *SimApp) setAnteHandler(txConfig client.TxConfig) {
anteHandler, err := ante.NewAnteHandler(
ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
SignModeHandler: txConfig.SignModeHandler(),
FeegrantKeeper: app.FeeGrantKeeper,
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
anteHandler, err := NewAnteHandler(
HandlerOptions{
ante.HandlerOptions{
AccountKeeper: app.AccountKeeper,
BankKeeper: app.BankKeeper,
SignModeHandler: txConfig.SignModeHandler(),
FeegrantKeeper: app.FeeGrantKeeper,
SigGasConsumer: ante.DefaultSigVerificationGasConsumer,
},
&app.CircuitKeeper,
},
)
if err != nil {
panic(err)
}
// Set the AnteHandler for the app
app.SetAnteHandler(anteHandler)
}

View File

@ -10,6 +10,7 @@ import (
authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1"
authzmodulev1 "cosmossdk.io/api/cosmos/authz/module/v1"
bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1"
circuitmodulev1 "cosmossdk.io/api/cosmos/circuit/module/v1"
consensusmodulev1 "cosmossdk.io/api/cosmos/consensus/module/v1"
crisismodulev1 "cosmossdk.io/api/cosmos/crisis/module/v1"
distrmodulev1 "cosmossdk.io/api/cosmos/distribution/module/v1"
@ -28,6 +29,7 @@ import (
vestingmodulev1 "cosmossdk.io/api/cosmos/vesting/module/v1"
"cosmossdk.io/depinject"
_ "cosmossdk.io/x/circuit" // import for side-effects
_ "cosmossdk.io/x/evidence" // import for side-effects
_ "cosmossdk.io/x/feegrant/module" // import for side-effects
_ "cosmossdk.io/x/nft/module" // import for side-effects
@ -48,6 +50,7 @@ import (
_ "github.com/cosmos/cosmos-sdk/x/staking" // import for side-effects
"cosmossdk.io/core/appconfig"
circuittypes "cosmossdk.io/x/circuit/types"
evidencetypes "cosmossdk.io/x/evidence/types"
"cosmossdk.io/x/feegrant"
"cosmossdk.io/x/nft"
@ -153,6 +156,7 @@ var (
upgradetypes.ModuleName,
vestingtypes.ModuleName,
consensustypes.ModuleName,
circuittypes.ModuleName,
},
// When ExportGenesis is not specified, the export genesis module order
// is equal to the init genesis order
@ -248,6 +252,10 @@ var (
Name: consensustypes.ModuleName,
Config: appconfig.WrapAny(&consensusmodulev1.Module{}),
},
{
Name: circuittypes.ModuleName,
Config: appconfig.WrapAny(&circuitmodulev1.Module{}),
},
},
}),
depinject.Supply(

View File

@ -12,6 +12,7 @@ import (
"cosmossdk.io/depinject"
storetypes "cosmossdk.io/store/types"
circuitkeeper "cosmossdk.io/x/circuit/keeper"
evidencekeeper "cosmossdk.io/x/evidence/keeper"
feegrantkeeper "cosmossdk.io/x/feegrant/keeper"
nftkeeper "cosmossdk.io/x/nft/keeper"
@ -81,6 +82,7 @@ type SimApp struct {
GroupKeeper groupkeeper.Keeper
NFTKeeper nftkeeper.Keeper
ConsensusParamsKeeper consensuskeeper.Keeper
CircuitBreakerKeeper circuitkeeper.Keeper
// simulation manager
sm *module.SimulationManager
@ -165,6 +167,7 @@ func NewSimApp(
&app.GroupKeeper,
&app.NFTKeeper,
&app.ConsensusParamsKeeper,
&app.CircuitBreakerKeeper,
); err != nil {
panic(err)
}

View File

@ -11,7 +11,8 @@ require (
cosmossdk.io/math v1.0.0
cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc
cosmossdk.io/tools/confix v0.0.0-20230120150717-4f6f6c00021f
cosmossdk.io/tools/rosetta v0.2.0
cosmossdk.io/tools/rosetta v0.2.1
cosmossdk.io/x/circuit v0.0.0-20230220112800-f69b9ff58fbe
cosmossdk.io/x/evidence v0.1.0
cosmossdk.io/x/feegrant v0.0.0-20230117113717-50e7c4a4ceff
cosmossdk.io/x/nft v0.0.0-20230113085233-fae3332d62fc
@ -168,7 +169,7 @@ require (
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/zondax/hid v0.9.1 // indirect
github.com/zondax/ledger-go v0.14.1 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
@ -203,6 +204,7 @@ replace (
cosmossdk.io/store => ../store
cosmossdk.io/tools/confix => ../tools/confix
cosmossdk.io/tools/rosetta => ../tools/rosetta
cosmossdk.io/x/circuit => ../x/circuit
cosmossdk.io/x/evidence => ../x/evidence
cosmossdk.io/x/feegrant => ../x/feegrant
cosmossdk.io/x/nft => ../x/nft

View File

@ -267,7 +267,7 @@ github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.5.1 h1:mixz5lJX4Hiz4FpqFREJHIXLfaLBntfaJv1h+/jS+Qg=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
@ -1078,8 +1078,8 @@ github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWp
github.com/zondax/ledger-go v0.14.1 h1:Pip65OOl4iJ84WTpA4BKChvOufMhhbxED3BaihoZN4c=
github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
@ -1328,7 +1328,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -39,6 +39,7 @@ require (
cloud.google.com/go/storage v1.30.0 // indirect
cosmossdk.io/client/v2 v2.0.0-20230309163709-87da587416ba // indirect
cosmossdk.io/collections v0.1.0 // indirect
cosmossdk.io/x/circuit v0.0.0-20230220112800-f69b9ff58fbe // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
@ -165,7 +166,7 @@ require (
github.com/ulikunitz/xz v0.5.11 // indirect
github.com/zondax/hid v0.9.1 // indirect
github.com/zondax/ledger-go v0.14.1 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/bbolt v1.3.7 // indirect
go.opencensus.io v0.24.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
@ -196,6 +197,7 @@ replace (
// TODO: remove me after collections v0.2.0 is released
cosmossdk.io/collections => ../collections
cosmossdk.io/store => ../store
cosmossdk.io/x/circuit => ../x/circuit
cosmossdk.io/x/evidence => ../x/evidence
cosmossdk.io/x/feegrant => ../x/feegrant
cosmossdk.io/x/nft => ../x/nft

View File

@ -269,7 +269,7 @@ github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf
github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/btcutil v1.1.2 h1:XLMbX8JQEiwMcYft2EGi8zPUkoa0abKIU6/BJSRsjzQ=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA=
github.com/bufbuild/protocompile v0.5.1 h1:mixz5lJX4Hiz4FpqFREJHIXLfaLBntfaJv1h+/jS+Qg=
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.8.0 h1:ea0Xadu+sHlu7x5O3gKhRpQ1IKiMrSiHttPF0ybECuA=
github.com/bytedance/sonic v1.8.0/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
@ -1080,8 +1080,8 @@ github.com/zondax/hid v0.9.1/go.mod h1:l5wttcP0jwtdLjqjMMWFVEE7d1zO0jvSPA9OPZxWp
github.com/zondax/ledger-go v0.14.1 h1:Pip65OOl4iJ84WTpA4BKChvOufMhhbxED3BaihoZN4c=
github.com/zondax/ledger-go v0.14.1/go.mod h1:fZ3Dqg6qcdXWSOJFKMG8GCTnD7slO/RL2feOQv8K320=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
@ -1328,7 +1328,6 @@ golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=

View File

@ -6,6 +6,7 @@ import (
authmodulev1 "cosmossdk.io/api/cosmos/auth/module/v1"
authzmodulev1 "cosmossdk.io/api/cosmos/authz/module/v1"
bankmodulev1 "cosmossdk.io/api/cosmos/bank/module/v1"
circuitmodulev1 "cosmossdk.io/api/cosmos/circuit/module/v1"
consensusmodulev1 "cosmossdk.io/api/cosmos/consensus/module/v1"
distrmodulev1 "cosmossdk.io/api/cosmos/distribution/module/v1"
evidencemodulev1 "cosmossdk.io/api/cosmos/evidence/module/v1"
@ -43,6 +44,7 @@ var beginBlockOrder = []string{
"params",
"consensus",
"vesting",
"circuit",
}
var endBlockersOrder = []string{
@ -64,6 +66,7 @@ var endBlockersOrder = []string{
"consensus",
"upgrade",
"vesting",
"circuit",
}
var initGenesisOrder = []string{
@ -85,6 +88,7 @@ var initGenesisOrder = []string{
"consensus",
"upgrade",
"vesting",
"circuit",
}
type appConfig struct {
@ -264,6 +268,15 @@ func NFTModule() ModuleOption {
}
}
func CircuitModule() ModuleOption {
return func(config *appConfig) {
config.moduleConfigs["circuit"] = &appv1alpha1.ModuleConfig{
Name: "circuit",
Config: appconfig.WrapAny(&circuitmodulev1.Module{}),
}
}
}
func OmitInitGenesis() ModuleOption {
return func(config *appConfig) {
config.setInitGenesis = false

View File

@ -98,9 +98,9 @@ func TestDeductFeesNoDelegation(t *testing.T) {
valid: true,
malleate: func(suite *AnteTestSuite) (TestAccount, sdk.AccAddress) {
accs := suite.CreateTestAccounts(2)
suite.feeGrantKeeper.EXPECT().UseGrantedFees(gomock.Any(), accs[1].acc.GetAddress(), accs[0].acc.GetAddress(), gomock.Any(), gomock.Any()).Return(nil).Times(2)
suite.bankKeeper.EXPECT().SendCoinsFromAccountToModule(gomock.Any(), accs[1].acc.GetAddress(), authtypes.FeeCollectorName, gomock.Any()).Return(nil).Times(2)
return accs[0], accs[1].acc.GetAddress()
},
},

View File

@ -87,7 +87,6 @@ https://github.com/cosmos/cosmos-sdk/blob/main/proto/cosmos/circuit/v1/tx.proto#
This message is expected to fail if:
* the granter is not an account with permission level `LEVEL_SUPER_ADMIN` or the module authority
* if the type urls does not exist <!-- TODO: is this possible?-->
### MsgTripCircuitBreaker
@ -98,7 +97,6 @@ https://github.com/cosmos/cosmos-sdk/blob/main/proto/cosmos/circuit/v1/tx.proto#
This message is expected to fail if:
* if the signer does not have a permission level with the ability to disable the specified type url message
* if the type urls does not exist <!-- TODO: is this possible?-->
### MsgResetCircuitBreaker
@ -108,8 +106,46 @@ https://github.com/cosmos/cosmos-sdk/blob/main/proto/cosmos/circuit/v1/tx.proto#
This message is expected to fail if:
* if the type urls does not exist <!-- TODO: is this possible?-->
* if the type url is not disabled
* `## Events` - list and describe event tags used
* `## Client` - list and describe CLI commands and gRPC and REST endpoints
## Events - list and describe event tags
The circuit module emits the following events:
### Message Events
#### MsgAuthorizeCircuitBreaker
| Type | Attribute Key | Attribute Value |
|---------|---------------|---------------------------|
| string | granter | {granteeAddress} |
| string | grantee | {granterAddress} |
| string | permission | {granteePermissions} |
| message | module | circuit |
| message | action | authorize_circuit_breaker |
#### MsgTripCircuitBreaker
| Type | Attribute Key | Attribute Value |
|----------|---------------|--------------------|
| string | authority | {authorityAddress} |
| []string | msg_urls | []string{msg_urls} |
| message | module | circuit |
| message | action | trip_circuit_breaker |
#### ResetCircuitBreaker
| Type | Attribute Key | Attribute Value |
|----------|---------------|--------------------|
| string | authority | {authorityAddress} |
| []string | msg_urls | []string{msg_urls} |
| message | module | circuit |
| message | action | reset_circuit_breaker |
## Keys - list of key prefixes used by the circuit module
* `AccountPermissionPrefix` - `0x01`
* `DisableListPrefix` - `0x02`
## Client - list and describe CLI commands and gRPC and REST endpoints

33
x/circuit/ante/circuit.go Normal file
View File

@ -0,0 +1,33 @@
package ante
import (
"github.com/cockroachdb/errors"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// CircuitBreaker is an interface that defines the methods for a circuit breaker.
type CircuitBreaker interface {
IsAllowed(ctx sdk.Context, typeURL string) bool
}
// CircuitBreakerDecorator is an AnteDecorator that checks if the transaction type is allowed to enter the mempool or be executed
type CircuitBreakerDecorator struct {
circuitKeeper CircuitBreaker
}
func NewCircuitBreakerDecorator(ck CircuitBreaker) CircuitBreakerDecorator {
return CircuitBreakerDecorator{
circuitKeeper: ck,
}
}
func (cbd CircuitBreakerDecorator) AnteHandle(ctx sdk.Context, tx sdk.Tx, simulate bool, next sdk.AnteHandler) (sdk.Context, error) {
// loop through all the messages and check if the message type is allowed
for _, msg := range tx.GetMsgs() {
if !cbd.circuitKeeper.IsAllowed(ctx, sdk.MsgTypeURL(msg)) {
return ctx, errors.New("tx type not allowed")
}
}
return next(ctx, tx, simulate)
}

View File

@ -0,0 +1,91 @@
package ante_test
import (
"testing"
storetypes "cosmossdk.io/store/types"
cbtypes "cosmossdk.io/x/circuit/types"
abci "github.com/cometbft/cometbft/abci/types"
cmproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/testutil"
clitestutil "github.com/cosmos/cosmos-sdk/testutil/cli"
"github.com/cosmos/cosmos-sdk/testutil/testdata"
moduletestutil "github.com/cosmos/cosmos-sdk/types/module/testutil"
"github.com/cosmos/cosmos-sdk/x/auth"
"github.com/cosmos/cosmos-sdk/x/bank"
"cosmossdk.io/x/circuit/ante"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/stretchr/testify/require"
)
type fixture struct {
ctx sdk.Context
mockStoreKey storetypes.StoreKey
mockMsgURL string
mockclientCtx client.Context
txBuilder client.TxBuilder
}
type MockCircuitBreaker struct {
isAllowed bool
}
func (m MockCircuitBreaker) IsAllowed(ctx sdk.Context, typeURL string) bool {
return typeURL == "/cosmos.circuit.v1.MsgAuthorizeCircuitBreaker"
}
func initFixture(t *testing.T) *fixture {
mockStoreKey := storetypes.NewKVStoreKey("test")
encCfg := moduletestutil.MakeTestEncodingConfig(auth.AppModuleBasic{}, bank.AppModuleBasic{})
mockclientCtx := client.Context{}.
WithTxConfig(encCfg.TxConfig).
WithClient(clitestutil.NewMockCometRPC(abci.ResponseQuery{}))
return &fixture{
ctx: testutil.DefaultContextWithDB(t, mockStoreKey, storetypes.NewTransientStoreKey("transient_test")).Ctx.WithBlockHeader(cmproto.Header{}),
mockStoreKey: mockStoreKey,
mockMsgURL: "test",
mockclientCtx: mockclientCtx,
txBuilder: mockclientCtx.TxConfig.NewTxBuilder(),
}
}
func TestCircuitBreakerDecorator(t *testing.T) {
t.Parallel()
f := initFixture(t)
_, _, addr1 := testdata.KeyTestPubAddr()
testcases := []struct {
msg sdk.Msg
allowed bool
}{
{msg: &cbtypes.MsgAuthorizeCircuitBreaker{
Grantee: "cosmos1fghij",
Granter: "cosmos1abcde",
}, allowed: true},
{msg: testdata.NewTestMsg(addr1), allowed: false},
}
for _, tc := range testcases {
// Circuit breaker is allowed to pass through all transactions
circuitBreaker := &MockCircuitBreaker{true}
// CircuitBreakerDecorator AnteHandler should always return success
decorator := ante.NewCircuitBreakerDecorator(circuitBreaker)
f.txBuilder.SetMsgs(tc.msg)
tx := f.txBuilder.GetTx()
_, err := decorator.AnteHandle(f.ctx, tx, false, func(ctx sdk.Context, tx sdk.Tx, simulate bool) (newCtx sdk.Context, err error) {
return ctx, nil
})
if tc.allowed {
require.NoError(t, err)
} else {
require.Equal(t, "tx type not allowed", err.Error())
}
}
}

View File

@ -0,0 +1,119 @@
package cli
import (
"cosmossdk.io/x/circuit/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
)
// GetQueryCmd returns the parent command for all circuit CLI query commands.
func GetQueryCmd() *cobra.Command {
cmd := &cobra.Command{
Use: types.ModuleName,
Short: "Querying commands for the circuit module",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
cmd.AddCommand(
GetDisabeListCmd(),
GetAccountCmd(),
GetAccountsCmd(),
)
return cmd
}
func GetDisabeListCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "disabled-list",
Short: "Query for all disabled message types",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.DisabledList(cmd.Context(), &types.QueryDisabledListRequest{})
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
func GetAccountCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "account [address]",
Short: "Query for account permissions",
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
addr, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.Account(cmd.Context(), &types.QueryAccountRequest{Address: addr.String()})
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}
func GetAccountsCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "accounts",
Short: "Query for all account permissions",
Args: cobra.ExactArgs(0),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientQueryContext(cmd)
if err != nil {
return err
}
pageReq, err := client.ReadPageRequest(cmd.Flags())
if err != nil {
return err
}
queryClient := types.NewQueryClient(clientCtx)
res, err := queryClient.Accounts(cmd.Context(), &types.QueryAccountsRequest{Pagination: pageReq})
if err != nil {
return err
}
return clientCtx.PrintProto(res)
},
}
flags.AddQueryFlagsToCmd(cmd)
return cmd
}

129
x/circuit/client/cli/tx.go Normal file
View File

@ -0,0 +1,129 @@
package cli
import (
"fmt"
"strings"
"github.com/cosmos/cosmos-sdk/version"
"cosmossdk.io/math"
"cosmossdk.io/x/circuit/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/flags"
"github.com/cosmos/cosmos-sdk/client/tx"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/spf13/cobra"
)
// NewTxCmd returns a root CLI command handler for all x/circuit transaction commands.
func NewTxCmd() *cobra.Command {
txCmd := &cobra.Command{
Use: types.ModuleName,
Short: "Circuit transaction subcommands",
DisableFlagParsing: true,
SuggestionsMinimumDistance: 2,
RunE: client.ValidateCmd,
}
txCmd.AddCommand(
AuthorizeCircuitBreakerCmd(),
TripCircuitBreakerCmd(),
)
return txCmd
}
// AuthorizeCircuitBreakerCmd returns a CLI command handler for creating a MsgAuthorizeCircuitBreaker transaction.
func AuthorizeCircuitBreakerCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "authorize [grantee] [permission_level] [limit_type_urls] --from [granter]",
Short: "Authorize an account to trip the circuit breaker.",
Long: `Authorize an account to trip the circuit breaker.
"SOME_MSGS" = 1,
"ALL_MSGS" = 2,
"SUPER_ADMIN" = 3,`,
Example: fmt.Sprintf(`%s circuit authorize [address] 0 "cosmos.bank.v1beta1.MsgSend,cosmos.bank.v1beta1.MsgMultiSend"`, version.AppName),
Args: cobra.RangeArgs(3, 4),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
grantee, err := sdk.AccAddressFromBech32(args[0])
if err != nil {
return err
}
lvl, err := math.ParseUint(args[1])
if err != nil {
return err
}
var typeUrls []string
if len(args) == 4 {
typeUrls = strings.Split(args[2], ",")
}
permission := types.Permissions{Level: types.Permissions_Level(lvl.Uint64()), LimitTypeUrls: typeUrls}
msg := types.NewMsgAuthorizeCircuitBreaker(clientCtx.GetFromAddress().String(), grantee.String(), &permission)
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
// TripCircuitBreakerCmd returns a CLI command handler for creating a MsgTripCircuitBreaker transaction.
func TripCircuitBreakerCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "disable [type_url]",
Short: "disable a message from being executed",
Example: fmt.Sprintf(`%s circuit disable "cosmos.bank.v1beta1.MsgSend,cosmos.bank.v1beta1.MsgMultiSend"`, version.AppName),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msg := types.NewMsgTripCircuitBreaker(clientCtx.GetFromAddress().String(), strings.Split(args[0], ","))
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}
// ResetCircuitBreakerCmd returns a CLI command handler for creating a MsgRestCircuitBreaker transaction.
func ResetCircuitBreakerCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "reset [type_url]",
Short: "Enable a message to be executed",
Example: fmt.Sprintf(`%s circuit reset "cosmos.bank.v1beta1.MsgSend,cosmos.bank.v1beta1.MsgMultiSend"`, version.AppName),
Args: cobra.ExactArgs(1),
RunE: func(cmd *cobra.Command, args []string) error {
clientCtx, err := client.GetClientTxContext(cmd)
if err != nil {
return err
}
msgTypeUrls := strings.Split(args[0], ",")
msg := types.NewMsgResetCircuitBreaker(clientCtx.GetFromAddress().String(), msgTypeUrls)
return tx.GenerateOrBroadcastTxCLI(clientCtx, cmd.Flags(), msg)
},
}
flags.AddTxFlagsToCmd(cmd)
return cmd
}

View File

@ -1,87 +0,0 @@
Feature: MsgAuthorizeCircuitBreaker
Circuit breaker actions can be authorized:
- when the granter is a super-admin
- when the permissions are valid
Rule: the granter must be a super-admin
Example: granter is a super-admin
Given "acct1" has permission "LEVEL_SUPER_ADMIN"
When "acct1" attempts to grant "acct2" the permissions
"""
{ "level": "LEVEL_ALL_MSGS" }
"""
Then expect success
Example: granter has no permissions
Given "acct1" has no permissions
When "acct1" attempts to grant "acct2" the permissions
"""
{ "level": "LEVEL_ALL_MSGS" }
"""
Then expect an "unauthorized" error
Example: granter has all msg's permissions
Given "acct1" has permission "LEVEL_ALL_MSGS"
When "acct1" attempts to grant "acct2" the permissions
"""
{ "level": "LEVEL_ALL_MSGS" }
"""
Then expect an "unauthorized" error
Rule: limit_msg_types must be used with LEVEL_SOME_MSGS
Example: granting LEVEL_SOME_MSGS with limit_msg_types
Given "acct1" has permission "LEVEL_ALL_MSGS"
When "acct1" attempts to grant "acct2" the permissions
"""
{
"level": "LEVEL_SOME_MSGS"
"limit_msg_types": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect success
Example: granting LEVEL_SOME_MSGS without limit_msg_types
Given "acct1" has permission "LEVEL_SUPER_ADMIN"
When "acct1" attempts to grant "acct2" the permissions
"""
{ "level": "LEVEL_SOME_MSGS" }
"""
Then expect an "invalid request" error
Example: granting LEVEL_ALL_MSGS with limit_msg_types
Given "acct1" has permission "LEVEL_SUPER_ADMIN"
When "acct1" attempts to grant "acct2" the permissions
"""
{
"level": "LEVEL_ALL_MSGS",
"limit_msg_types": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect an "invalid request" error
Example: attempting to revoke with limit_msg_types
Given "acct1" has permission "LEVEL_SUPER_ADMIN"
When "acct1" attempts to revoke "acct2" the permissions
"""
{
"level": "LEVEL_NONE_UNSPECIFIED",
"limit_msg_types": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect an "invalid request" error
Rule: permissions can be revoked using LEVEL_NONE_UNSPECIFIED
Example: revoking permissions
Given "acct1" has permission "LEVEL_SUPER_ADMIN"
And "acct2" has permission "LEVEL_ALL_MSGS"
When "acct1" attempts to revoke "acct2" the permissions
"""
{
"level": "LEVEL_NONE_UNSPECIFIED",
}
"""
Then expect sucesss
And expect that "acct2" has no permissions

View File

@ -1,65 +0,0 @@
Feature: MsgResetCircuitBreaker
- Circuit breaker can be reset:
- when the permissions are valid
Rule: caller must have a permission to reset the circuit
Example: caller attempts to reset a disabled message
Given "acct1" has permission "LEVEL_SUPER_ADMIN"
When "acct1" attempts to enable a disabled message
"""
{
"msg": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect success
Example: caller has no permissions
Given "acct1" has no permissions
When "acct1" attempts to reset a disabled message
"""
{
"msg": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect an "unauthorized" error
Example: caller attempts to reset a disabled message
Given "acct1" has permission "LEVEL_ALL_MSGS"
When "acct1" attempts to reset a disabled message
"""
{
"msg": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect success
Example: caller attempts to reset a message they have permission to trip
Given "acct1" has permission to trip circuit breaker for "cosmos.bank.v1beta1.MsgSend"
When "acct1" attempts to reset a disabled message
"""
{
"msg": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect success
Example: caller attempts to reset a message they don't have permission to trip
Given "acct1" has permission to trip circuit breaker for "cosmos.bank.v1beta1.MsgSend"
When "acct1" attempts to reset a disabled message
"""
{
"msg": "cosmos.bank.v1beta1.MultiSend"
}
"""
Then expect success
Example: caller attempts to reset a message that has been tripped
Given "acct1" has permission "LEVEL_SUPER_ADMIN" & "cosmos.bank.v1beta1.MultiSend" has been enabled
When "acct1" attempts to reset a disabled message
"""
{
"msg": "cosmos.bank.v1beta1.MultiSend"
}
"""
Then expect an "msg enabled" error

View File

@ -1,76 +0,0 @@
Feature: MsgTripCircuitBreaker
Circuit breaker can disable message execution:
- when the caller trips the circuitbreaker for a message(s)
- when the caller has the correct permissions
Rule: a user must have permission to trip the circuit breaker for a message(s)
Example: user is a super admin
Given "acct1" has permission "LEVEL_SUPER_ADMIN"
When "acct1" attempts to disable msg execution
"""
{
"msg": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect success
Example: user has no permissions
Given "acct1" has no permissions
When "acct1" attempts to disable msg execution
"""
{
"msg": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect an "unauthorized" error
Example: user has permission for all messages
Given "acct1" has permission "LEVEL_ALL_MSGS"
When "acct1" attempts to disable msg execution
"""
{
"msg": "cosmos.bank.v1beta1.MsgSend"
}
"""
Then expect success
Example: user has permission for the messages
Given "acct1" has permission to disable "cosmos.bank.v1beta1.MsgSend" and "cosmos.staking.v1beta1.MsgDelegate"
When "acct1" attempts to disable msg execution
"""
{
"msgs": ["cosmos.bank.v1beta1.MsgSend",cosmos.staking.v1beta1.MsgDelegate"]
}
"""
Then expect success
Example: user does not have permission for 1 of the messages in the list
Given "acct1" has permission to disable "cosmos.bank.v1beta1.MsgSend"
When "acct1" attempts to disable msg execution
"""
{
"msgs": ["cosmos.bank.v1beta1.MsgSend","cosmos.staking.v1beta1.MsgCreateValidator"]
}
"""
Then expect an "unauthorized" error
Example: user does not have permission for the message
Given "acct1" has permission to diable "cosmos.bank.v1beta1.MsgSend"
When "acct1" attempts to disable msg execution
"""
{
"msg": "cosmos.bank.v1beta1.MultiSend"
}
"""
Then expect an "unauthorized" error
Example: user tries to trip an already tripped circuit breaker
Given "acct1" has permission to diable "cosmos.bank.v1beta1.MsgSend" & is already tripped
When "acct1" attempts to disable msg execution
"""
{
"msg": "cosmos.bank.v1beta1.MultiSend"
}
"""
Then expect an "msg disabled" error

View File

@ -1,91 +1,163 @@
module github.com/cosmos/cosmos-sdk/x/circuit
module cosmossdk.io/x/circuit
go 1.20
require (
cosmossdk.io/api v0.4.1
cosmossdk.io/core v0.7.0
cosmossdk.io/depinject v1.0.0-alpha.3
cosmossdk.io/errors v1.0.0-beta.7.0.20230429155654-3ee8242364e4
cosmossdk.io/math v1.0.0
cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc
github.com/cockroachdb/errors v1.9.1
github.com/cometbft/cometbft v0.37.1
github.com/cosmos/cosmos-sdk v0.46.0-beta2.0.20230424095137-b73c17cb9cc8
github.com/cosmos/gogoproto v1.4.10
github.com/golang/protobuf v1.5.3
github.com/grpc-ecosystem/grpc-gateway v1.16.0
github.com/regen-network/gocuke v0.6.2
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.8.2
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1
google.golang.org/grpc v1.55.0
gotest.tools/v3 v3.4.0
)
require (
cosmossdk.io/api v0.4.1 // indirect
cosmossdk.io/collections v0.1.0 // indirect
cosmossdk.io/core v0.7.0 // indirect
cosmossdk.io/depinject v1.0.0-alpha.3 // indirect
cosmossdk.io/errors v1.0.0-beta.7 // indirect
cosmossdk.io/log v1.1.0 // indirect
cosmossdk.io/math v1.0.0 // indirect
cosmossdk.io/store v0.1.0-alpha.1.0.20230328185921-37ba88872dbc // indirect
cosmossdk.io/x/tx v0.5.5 // indirect
cosmossdk.io/x/tx v0.6.3 // indirect
filippo.io/edwards25519 v1.0.0 // indirect
github.com/99designs/go-keychain v0.0.0-20191008050251-8e49817e8af4 // indirect
github.com/99designs/keyring v1.2.1 // indirect
github.com/ChainSafe/go-schnorrkel v0.0.0-20200405005733-88cbf1b4c40d // indirect
github.com/DataDog/zstd v1.5.2 // indirect
github.com/alecthomas/participle/v2 v2.0.0-alpha7 // indirect
github.com/armon/go-metrics v0.4.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/speakeasy v0.1.1-0.20220910012023-760eaf8b6816 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/bufbuild/protocompile v0.5.1 // indirect
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
github.com/cespare/xxhash v1.1.0 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cockroachdb/apd/v3 v3.1.0 // indirect
github.com/cockroachdb/errors v1.9.1 // indirect
github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect
github.com/cockroachdb/pebble v0.0.0-20230412222916-60cfeb46143b // indirect
github.com/cockroachdb/redact v1.1.3 // indirect
github.com/cometbft/cometbft v0.37.1 // indirect
github.com/cometbft/cometbft-db v0.7.0 // indirect
github.com/confio/ics23/go v0.9.0 // indirect
github.com/cosmos/btcutil v1.0.5 // indirect
github.com/cosmos/cosmos-db v1.0.0-rc.1 // indirect
github.com/cosmos/cosmos-proto v1.0.0-beta.3 // indirect
github.com/cucumber/common/gherkin/go/v22 v22.0.0 // indirect
github.com/cucumber/common/messages/go/v17 v17.1.1 // indirect
github.com/cosmos/go-bip39 v1.0.0 // indirect
github.com/cosmos/gogogateway v1.2.0 // indirect
github.com/cosmos/iavl v0.21.0 // indirect
github.com/cosmos/ics23/go v0.10.0 // indirect
github.com/cosmos/ledger-cosmos-go v0.13.0 // indirect
github.com/creachadair/taskgroup v0.4.2 // indirect
github.com/danieljoos/wincred v1.1.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/dgraph-io/badger/v2 v2.2007.4 // indirect
github.com/dgraph-io/ristretto v0.1.1 // indirect
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/dvsekhvalnov/jose2go v1.5.0 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/felixge/httpsnoop v1.0.2 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/getsentry/sentry-go v0.20.0 // indirect
github.com/go-kit/kit v0.12.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.6.0 // indirect
github.com/gofrs/uuid v4.3.0+incompatible // indirect
github.com/godbus/dbus v0.0.0-20190726142602-4481cbc300e2 // indirect
github.com/gogo/googleapis v1.4.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/glog v1.1.0 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/btree v1.1.2 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/google/orderedcode v0.0.1 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/mux v1.8.0 // indirect
github.com/gorilla/websocket v1.5.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/gtank/ristretto255 v0.1.2 // indirect
github.com/hashicorp/go-hclog v1.5.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-plugin v1.4.9 // indirect
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/hdevalence/ed25519consensus v0.1.0 // indirect
github.com/huandu/skiplist v1.2.0 // indirect
github.com/iancoleman/strcase v0.2.0 // indirect
github.com/improbable-eng/grpc-web v0.15.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jmhodges/levigo v1.0.0 // indirect
github.com/klauspost/compress v1.16.5 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lib/pq v1.10.7 // indirect
github.com/libp2p/go-buffer-pool v0.1.0 // indirect
github.com/linxGnu/grocksdb v1.7.16 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.18 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mimoo/StrobeGo v0.0.0-20210601165009-122bf33a46e0 // indirect
github.com/minio/highwayhash v1.0.2 // indirect
github.com/mitchellh/go-testing-interface v1.14.1 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/mtibben/percent v0.2.1 // indirect
github.com/oklog/run v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.0.7 // indirect
github.com/petermattis/goid v0.0.0-20230317030725-371a4b8eda08 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/prometheus/client_golang v1.15.1 // indirect
github.com/prometheus/client_model v0.4.0 // indirect
github.com/prometheus/common v0.43.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475 // indirect
github.com/rogpeppe/go-internal v1.10.0 // indirect
github.com/rs/cors v1.8.3 // indirect
github.com/rs/zerolog v1.29.1 // indirect
github.com/sasha-s/go-deadlock v0.3.1 // indirect
github.com/spf13/afero v1.9.3 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.15.0 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/syndtr/goleveldb v1.0.1-0.20220721030215-126854af5e6d // indirect
github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c // indirect
github.com/tendermint/go-amino v0.16.0 // indirect
github.com/tidwall/btree v1.6.0 // indirect
github.com/zondax/hid v0.9.1 // indirect
github.com/zondax/ledger-go v0.14.1 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sync v0.2.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/term v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
nhooyr.io/websocket v1.8.6 // indirect
pgregory.net/rapid v0.5.7 // indirect
sigs.k8s.io/yaml v1.3.0 // indirect
)
replace (
cosmossdk.io/x/tx => ../tx
github.com/cosmos/cosmos-sdk => ../../.
cosmossdk.io/store => ../../store
cosmossdk.io/collections => ../../collections
)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,56 @@
package keeper
import (
"cosmossdk.io/x/circuit/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
func (k *Keeper) ExportGenesis(ctx sdk.Context) (data *types.GenesisState) {
var (
permissions []*types.GenesisAccountPermissions
disabledMsgs []string
)
k.IteratePermissions(ctx, func(address []byte, perm types.Permissions) (stop bool) {
add, err := k.addressCodec.BytesToString(address)
if err != nil {
panic(err)
}
// Convert the Permissions struct to a GenesisAccountPermissions struct
// and add it to the permissions slice
permissions = append(permissions, &types.GenesisAccountPermissions{
Address: add,
Permissions: &perm,
})
return false
})
k.IterateDisableLists(ctx, func(address []byte, perm types.Permissions) (stop bool) {
disabledMsgs = append(disabledMsgs, perm.LimitTypeUrls...)
return false
})
return &types.GenesisState{
AccountPermissions: permissions,
DisabledTypeUrls: disabledMsgs,
}
}
// InitGenesis initializes the bank module's state from a given genesis state.
func (k *Keeper) InitGenesis(ctx sdk.Context, genState *types.GenesisState) {
for _, accounts := range genState.AccountPermissions {
add, err := k.addressCodec.StringToBytes(accounts.Address)
if err != nil {
panic(err)
}
// Set the permissions for the account
if err := k.SetPermissions(ctx, add, accounts.Permissions); err != nil {
panic(err)
}
}
for _, url := range genState.DisabledTypeUrls {
// Set the disabled type urls
k.DisableMsg(ctx, url)
}
}

125
x/circuit/keeper/keeper.go Normal file
View File

@ -0,0 +1,125 @@
package keeper
import (
proto "github.com/cosmos/gogoproto/proto"
"cosmossdk.io/core/address"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/circuit/types"
sdk "github.com/cosmos/cosmos-sdk/types"
)
// Keeper defines the circuit module's keeper.
type Keeper struct {
storekey storetypes.StoreKey
authority []byte
addressCodec address.Codec
}
// NewKeeper constructs a new Circuit Keeper instance
func NewKeeper(storeKey storetypes.StoreKey, authority string, addressCodec address.Codec) Keeper {
auth, err := addressCodec.StringToBytes(authority)
if err != nil {
panic(err)
}
return Keeper{
storekey: storeKey,
authority: auth,
addressCodec: addressCodec,
}
}
func (k *Keeper) GetAuthority() []byte {
return k.authority
}
func (k *Keeper) GetPermissions(ctx sdk.Context, address []byte) (*types.Permissions, error) {
store := ctx.KVStore(k.storekey)
key := types.CreateAddressPrefix(address)
bz := store.Get(key)
perms := &types.Permissions{}
if err := proto.Unmarshal(bz, perms); err != nil {
return &types.Permissions{}, err
}
return perms, nil
}
func (k *Keeper) SetPermissions(ctx sdk.Context, address []byte, perms *types.Permissions) error {
store := ctx.KVStore(k.storekey)
bz, err := proto.Marshal(perms)
if err != nil {
return err
}
key := types.CreateAddressPrefix(address)
store.Set(key, bz)
return nil
}
func (k *Keeper) IsAllowed(ctx sdk.Context, msgURL string) bool {
store := ctx.KVStore(k.storekey)
return !store.Has(types.CreateDisableMsgPrefix(msgURL))
}
func (k *Keeper) DisableMsg(ctx sdk.Context, msgURL string) {
ctx.KVStore(k.storekey).Set(types.CreateDisableMsgPrefix(msgURL), []byte{})
}
func (k *Keeper) EnableMsg(ctx sdk.Context, msgURL string) {
ctx.KVStore(k.storekey).Delete(types.CreateDisableMsgPrefix(msgURL))
}
func (k *Keeper) IteratePermissions(ctx sdk.Context, cb func(address []byte, perms types.Permissions) (stop bool)) {
store := ctx.KVStore(k.storekey)
iter := storetypes.KVStorePrefixIterator(store, types.AccountPermissionPrefix)
defer func(iter storetypes.Iterator) {
err := iter.Close()
if err != nil {
return
}
}(iter)
for ; iter.Valid(); iter.Next() {
var perms types.Permissions
err := proto.Unmarshal(iter.Value(), &perms)
if err != nil {
panic(err)
}
if cb(iter.Key()[len(types.AccountPermissionPrefix):], perms) {
break
}
}
}
func (k *Keeper) IterateDisableLists(ctx sdk.Context, cb func(url []byte, perms types.Permissions) (stop bool)) {
store := ctx.KVStore(k.storekey)
iter := storetypes.KVStorePrefixIterator(store, types.AccountPermissionPrefix)
defer func(iter storetypes.Iterator) {
err := iter.Close()
if err != nil {
return
}
}(iter)
for ; iter.Valid(); iter.Next() {
var perms types.Permissions
err := proto.Unmarshal(iter.Value(), &perms)
if err != nil {
panic(err)
}
if cb(iter.Key()[len(types.DisableListPrefix):], perms) {
break
}
}
}

View File

@ -0,0 +1,135 @@
package keeper_test
import (
"bytes"
"testing"
cmproto "github.com/cometbft/cometbft/proto/tendermint/types"
"github.com/stretchr/testify/require"
storetypes "cosmossdk.io/store/types"
"cosmossdk.io/x/circuit/keeper"
"cosmossdk.io/x/circuit/types"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
)
type fixture struct {
ctx sdk.Context
keeper keeper.Keeper
mockAddr []byte
mockPerms types.Permissions
mockMsgURL string
}
func initFixture(t *testing.T) *fixture {
ac := addresscodec.NewBech32Codec("cosmos")
mockStoreKey := storetypes.NewKVStoreKey("test")
k := keeper.NewKeeper(mockStoreKey, authtypes.NewModuleAddress("gov").String(), ac)
bz, err := ac.StringToBytes(authtypes.NewModuleAddress("gov").String())
require.NoError(t, err)
return &fixture{
ctx: testutil.DefaultContextWithDB(t, mockStoreKey, storetypes.NewTransientStoreKey("transient_test")).Ctx.WithBlockHeader(cmproto.Header{}),
keeper: k,
mockAddr: bz,
mockPerms: types.Permissions{
Level: 3,
},
mockMsgURL: "mock_url",
}
}
func TestGetAuthority(t *testing.T) {
t.Parallel()
f := initFixture(t)
authority := f.keeper.GetAuthority()
require.True(t, bytes.Equal(f.mockAddr, authority))
}
func TestGetAndSetPermissions(t *testing.T) {
t.Parallel()
f := initFixture(t)
// Set the permissions for the mock address.
err := f.keeper.SetPermissions(f.ctx, f.mockAddr, &f.mockPerms)
require.NoError(t, err)
// Retrieve the permissions for the mock address.
perms, err := f.keeper.GetPermissions(f.ctx, f.mockAddr)
require.NoError(t, err)
//// Assert that the retrieved permissions match the expected value.
require.Equal(t, &f.mockPerms, perms)
}
func TestIteratePermissions(t *testing.T) {
t.Parallel()
f := initFixture(t)
// Define a set of mock permissions
mockPerms := []types.Permissions{
{Level: types.Permissions_LEVEL_SOME_MSGS, LimitTypeUrls: []string{"url1", "url2"}},
{Level: types.Permissions_LEVEL_ALL_MSGS},
{Level: types.Permissions_LEVEL_NONE_UNSPECIFIED},
}
// Set the permissions for a set of mock addresses
mockAddrs := [][]byte{
[]byte("mock_address_1"),
[]byte("mock_address_2"),
[]byte("mock_address_3"),
}
for i, addr := range mockAddrs {
f.keeper.SetPermissions(f.ctx, addr, &mockPerms[i])
}
// Define a variable to store the returned permissions
var returnedPerms []types.Permissions
// Iterate through the permissions and append them to the returnedPerms slice
f.keeper.IteratePermissions(f.ctx, func(address []byte, perms types.Permissions) (stop bool) {
returnedPerms = append(returnedPerms, perms)
return false
})
// Assert that the returned permissions match the set mock permissions
require.Equal(t, mockPerms, returnedPerms)
}
func TestIterateDisabledList(t *testing.T) {
t.Parallel()
f := initFixture(t)
mockPerms := []types.Permissions{
{Level: types.Permissions_LEVEL_SUPER_ADMIN, LimitTypeUrls: []string{"url1", "url2"}},
{Level: types.Permissions_LEVEL_ALL_MSGS},
{Level: types.Permissions_LEVEL_NONE_UNSPECIFIED},
}
// Set the permissions for a set of mock addresses
mockAddrs := [][]byte{
[]byte("mock_address_1"),
[]byte("mock_address_2"),
[]byte("mock_address_3"),
}
for i, addr := range mockAddrs {
f.keeper.SetPermissions(f.ctx, addr, &mockPerms[i])
}
// Define a variable to store the returned disabled URLs
var returnedDisabled []types.Permissions
f.keeper.IterateDisableLists(f.ctx, func(address []byte, perms types.Permissions) bool {
returnedDisabled = append(returnedDisabled, perms)
return false
})
// Assert that the returned disabled URLs match the set mock disabled URLs
require.Equal(t, mockPerms[0].LimitTypeUrls, returnedDisabled[0].LimitTypeUrls)
require.Equal(t, mockPerms[1].LimitTypeUrls, returnedDisabled[1].LimitTypeUrls)
require.Equal(t, mockPerms[2].LimitTypeUrls, returnedDisabled[2].LimitTypeUrls)
}

View File

@ -1,45 +0,0 @@
package keeper
import (
"testing"
"github.com/regen-network/gocuke"
"gotest.tools/v3/assert"
)
func TestAuthorize(t *testing.T) {
t.Skip("TODO: uncomment this after implementing")
gocuke.NewRunner(t, &authorizeSuite{}).Path("../features/msg_authorize.feature").Run()
}
type authorizeSuite struct {
*baseFixture
}
func (s *authorizeSuite) Before(t *testing.T) {
s.baseFixture = initFixture(t)
}
func (s *authorizeSuite) HasPermission(a, b string) {
panic("PENDING")
}
func (s *authorizeSuite) HasNoPermissions(a string) {
panic("PENDING")
}
func (s *authorizeSuite) AttemptsToGrantThePermissions(a, b string, c gocuke.DocString) {
panic("PENDING")
}
func (s *authorizeSuite) ExpectSuccess() {
assert.NilError(s.t, s.err)
}
func (s *authorizeSuite) ExpectAnError(a string) {
assert.Error(s.t, s.err, a)
}
func (s *authorizeSuite) ExpectThatHasNoPermissions(a string) {
panic("PENDING")
}

View File

@ -1,45 +0,0 @@
package keeper
import (
"testing"
"github.com/regen-network/gocuke"
"gotest.tools/v3/assert"
)
func TestReset(t *testing.T) {
t.Skip("TODO: uncomment this after implementing")
gocuke.NewRunner(t, &resetSuite{}).Path("../features/msg_reset.feature").Run()
}
type resetSuite struct {
*baseFixture
}
func (s *resetSuite) Before(t *testing.T) {
s.baseFixture = initFixture(t)
}
func (s *resetSuite) HasPermission(a, b string) {
panic("PENDING")
}
func (s *resetSuite) HasNoPermissions(a string) {
panic("PENDING")
}
func (s *resetSuite) AttemptsToResetCircuit(a, b string, c gocuke.DocString) {
panic("PENDING")
}
func (s *resetSuite) ExpectSuccess() {
assert.NilError(s.t, s.err)
}
func (s *resetSuite) ExpectAnError(a string) {
assert.Error(s.t, s.err, a)
}
func (s *resetSuite) ExpectThatHasNoPermissions(a string) {
panic("PENDING")
}

View File

@ -0,0 +1,179 @@
package keeper
import (
"bytes"
context "context"
fmt "fmt"
"strings"
errorsmod "cosmossdk.io/errors"
"cosmossdk.io/x/circuit/types"
sdk "github.com/cosmos/cosmos-sdk/types"
sdkerrors "github.com/cosmos/cosmos-sdk/types/errors"
)
type msgServer struct {
Keeper
}
var _ types.MsgServer = msgServer{}
// NewMsgServerImpl returns an implementation of the bank MsgServer interface
// for the provided Keeper.
func NewMsgServerImpl(keeper Keeper) types.MsgServer {
return &msgServer{Keeper: keeper}
}
func (srv msgServer) AuthorizeCircuitBreaker(goCtx context.Context, msg *types.MsgAuthorizeCircuitBreaker) (*types.MsgAuthorizeCircuitBreakerResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
address, err := srv.addressCodec.StringToBytes(msg.Granter)
if err != nil {
return nil, err
}
// if the granter is the module authority no need to check perms
if !bytes.Equal(address, srv.GetAuthority()) {
// Check that the authorizer has the permission level of "super admin"
perms, err := srv.GetPermissions(ctx, address)
if err != nil {
return nil, fmt.Errorf("user permission does not exist %w", err)
}
if perms.Level != types.Permissions_LEVEL_SUPER_ADMIN {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "only super admins can authorize users")
}
}
grantee, err := srv.addressCodec.StringToBytes(msg.Grantee)
if err != nil {
return nil, err
}
// Append the account in the msg to the store's set of authorized super admins
if err = srv.SetPermissions(ctx, grantee, msg.Permissions); err != nil {
return nil, err
}
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
"authorize_circuit_breaker",
sdk.NewAttribute("granter", msg.Granter),
sdk.NewAttribute("grantee", msg.Grantee),
sdk.NewAttribute("permission", msg.Permissions.String()),
),
})
return &types.MsgAuthorizeCircuitBreakerResponse{
Success: true,
}, nil
}
func (srv msgServer) TripCircuitBreaker(goCtx context.Context, msg *types.MsgTripCircuitBreaker) (*types.MsgTripCircuitBreakerResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
address, err := srv.addressCodec.StringToBytes(msg.Authority)
if err != nil {
return nil, err
}
// Check that the account has the permissions
perms, err := srv.GetPermissions(ctx, address)
if err != nil {
return nil, fmt.Errorf("user permission does not exist %w", err)
}
store := ctx.KVStore(srv.storekey)
switch {
case perms.Level == types.Permissions_LEVEL_SUPER_ADMIN || perms.Level == types.Permissions_LEVEL_ALL_MSGS || bytes.Equal(address, srv.GetAuthority()):
for _, msgTypeURL := range msg.MsgTypeUrls {
// check if the message is in the list of allowed messages
if !srv.IsAllowed(ctx, msgTypeURL) {
return nil, fmt.Errorf("message %s is already disabled", msgTypeURL)
}
store.Set(types.CreateDisableMsgPrefix(msgTypeURL), []byte{0x01})
}
case perms.Level == types.Permissions_LEVEL_SOME_MSGS:
for _, msgTypeURL := range msg.MsgTypeUrls {
// check if the message is in the list of allowed messages
if !srv.IsAllowed(ctx, msgTypeURL) {
return nil, fmt.Errorf("message %s is already disabled", msgTypeURL)
}
for _, msgurl := range perms.LimitTypeUrls {
if msgTypeURL == msgurl {
store.Set(types.CreateDisableMsgPrefix(msgTypeURL), []byte{0x01})
} else {
return nil, errorsmod.Wrapf(sdkerrors.ErrUnauthorized, "account does not have permission to trip circuit breaker for message %s", msgTypeURL)
}
}
}
default:
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "account does not have permission to trip circuit breaker")
}
var urls string
if len(msg.GetMsgTypeUrls()) > 1 {
urls = strings.Join(msg.GetMsgTypeUrls(), ",")
}
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
"trip_circuit_breaker",
sdk.NewAttribute("authority", msg.Authority),
sdk.NewAttribute("msg_url", urls),
),
})
return &types.MsgTripCircuitBreakerResponse{
Success: true,
}, nil
}
// ResetCircuitBreaker resumes processing of Msg's in the state machine that
// have been been paused using TripCircuitBreaker.
func (srv msgServer) ResetCircuitBreaker(goCtx context.Context, msg *types.MsgResetCircuitBreaker) (*types.MsgResetCircuitBreakerResponse, error) {
ctx := sdk.UnwrapSDKContext(goCtx)
keeper := srv.Keeper
address, err := srv.addressCodec.StringToBytes(msg.Authority)
if err != nil {
return nil, err
}
// Get the permissions for the account specified in the msg.Authority field
perms, err := keeper.GetPermissions(ctx, address)
if err != nil {
return nil, fmt.Errorf("user permission does not exist %w", err)
}
store := ctx.KVStore(srv.storekey)
if perms.Level == types.Permissions_LEVEL_SUPER_ADMIN || perms.Level == types.Permissions_LEVEL_ALL_MSGS || perms.Level == types.Permissions_LEVEL_SOME_MSGS || bytes.Equal(address, srv.GetAuthority()) {
// add all msg type urls to the disable list
for _, msgTypeURL := range msg.MsgTypeUrls {
if srv.IsAllowed(ctx, msgTypeURL) {
return nil, fmt.Errorf("message %s is not disabled", msgTypeURL)
}
store.Delete(types.CreateDisableMsgPrefix(msgTypeURL))
}
} else {
return nil, errorsmod.Wrap(sdkerrors.ErrUnauthorized, "account does not have permission to reset circuit breaker")
}
var urls string
if len(msg.GetMsgTypeUrls()) > 1 {
urls = strings.Join(msg.GetMsgTypeUrls(), ",")
}
ctx.EventManager().EmitEvents(sdk.Events{
sdk.NewEvent(
"reset_circuit_breaker",
sdk.NewAttribute("authority", msg.Authority),
sdk.NewAttribute("msg_url", urls),
),
})
return &types.MsgResetCircuitBreakerResponse{Success: true}, nil
}

View File

@ -0,0 +1,223 @@
package keeper
import (
"testing"
"cosmossdk.io/x/circuit/types"
"github.com/stretchr/testify/require"
)
const msgSend = "cosmos.bank.v1beta1.MsgSend"
func Test_AuthorizeCircuitBreaker(t *testing.T) {
ft := setupFixture(t)
srv := msgServer{
Keeper: ft.Keeper,
}
// add a new super admin
adminPerms := &types.Permissions{Level: types.Permissions_LEVEL_SUPER_ADMIN, LimitTypeUrls: []string{""}}
msg := &types.MsgAuthorizeCircuitBreaker{Granter: addresses[0], Grantee: addresses[1], Permissions: adminPerms}
_, err := srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.NoError(t, err)
add1, err := ft.Keeper.addressCodec.StringToBytes(addresses[1])
require.NoError(t, err)
perms, err := ft.Keeper.GetPermissions(ft.Ctx, add1)
require.NoError(t, err)
require.Equal(t, adminPerms, perms, "admin perms are not the same")
// add a super user
allmsgs := &types.Permissions{Level: types.Permissions_LEVEL_ALL_MSGS, LimitTypeUrls: []string{""}}
msg = &types.MsgAuthorizeCircuitBreaker{Granter: addresses[0], Grantee: addresses[2], Permissions: allmsgs}
_, err = srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.NoError(t, err)
add2, err := ft.Keeper.addressCodec.StringToBytes(addresses[2])
require.NoError(t, err)
perms, err = ft.Keeper.GetPermissions(ft.Ctx, add2)
require.NoError(t, err)
require.Equal(t, allmsgs, perms, "admin perms are not the same")
// unauthorized user who does not have perms trying to authorize
superPerms := &types.Permissions{Level: types.Permissions_LEVEL_SUPER_ADMIN, LimitTypeUrls: []string{}}
msg = &types.MsgAuthorizeCircuitBreaker{Granter: addresses[3], Grantee: addresses[2], Permissions: superPerms}
_, err = srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.Error(t, err, "user with no permission should fail in authorizing others")
// user with permission level all_msgs tries to grant another user perms
somePerms := &types.Permissions{Level: types.Permissions_LEVEL_SOME_MSGS, LimitTypeUrls: []string{}}
msg = &types.MsgAuthorizeCircuitBreaker{Granter: addresses[2], Grantee: addresses[3], Permissions: somePerms}
_, err = srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.Error(t, err, "user[2] does not have permission to grant others permission")
// user successfully grants another user perms to a specific permission
somemsgs := &types.Permissions{Level: types.Permissions_LEVEL_SOME_MSGS, LimitTypeUrls: []string{msgSend}}
msg = &types.MsgAuthorizeCircuitBreaker{Granter: addresses[0], Grantee: addresses[3], Permissions: somemsgs}
_, err = srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.NoError(t, err)
add3, err := ft.Keeper.addressCodec.StringToBytes(addresses[3])
require.NoError(t, err)
perms, err = ft.Keeper.GetPermissions(ft.Ctx, add3)
require.NoError(t, err)
require.Equal(t, somemsgs, perms, "admin perms are not the same")
add4, err := ft.Keeper.addressCodec.StringToBytes(addresses[4])
require.NoError(t, err)
perms, err = ft.Keeper.GetPermissions(ft.Ctx, add4)
require.NoError(t, err)
require.Equal(t, &types.Permissions{Level: types.Permissions_LEVEL_NONE_UNSPECIFIED, LimitTypeUrls: nil}, perms, "user should have no perms by default")
// admin tries grants another user permission ALL_MSGS with limited urls populated
invalidmsgs := &types.Permissions{Level: types.Permissions_LEVEL_SOME_MSGS, LimitTypeUrls: []string{msgSend}}
msg = &types.MsgAuthorizeCircuitBreaker{Granter: addresses[0], Grantee: addresses[4], Permissions: invalidmsgs}
_, err = srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.NoError(t, err)
}
func Test_TripCircuitBreaker(t *testing.T) {
ft := setupFixture(t)
srv := msgServer{
Keeper: ft.Keeper,
}
url := msgSend
// admin trips circuit breaker
admintrip := &types.MsgTripCircuitBreaker{Authority: addresses[0], MsgTypeUrls: []string{url}}
_, err := srv.TripCircuitBreaker(ft.Ctx, admintrip)
require.NoError(t, err)
allowed := ft.Keeper.IsAllowed(ft.Ctx, url)
require.False(t, allowed, "circuit breaker should be tripped")
// user with all messages trips circuit breaker
// add a super user
allmsgs := &types.Permissions{Level: types.Permissions_LEVEL_ALL_MSGS, LimitTypeUrls: []string{""}}
msg := &types.MsgAuthorizeCircuitBreaker{Granter: addresses[0], Grantee: addresses[1], Permissions: allmsgs}
_, err = srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.NoError(t, err)
// try to trip the circuit breaker
url2 := "cosmos.staking.v1beta1.MsgDelegate"
superTrip := &types.MsgTripCircuitBreaker{Authority: addresses[1], MsgTypeUrls: []string{url2}}
_, err = srv.TripCircuitBreaker(ft.Ctx, superTrip)
require.NoError(t, err)
allowed = ft.Keeper.IsAllowed(ft.Ctx, url2)
require.False(t, allowed, "circuit breaker should be tripped")
// user with no permission attempts to trips circuit breaker
unknownTrip := &types.MsgTripCircuitBreaker{Authority: addresses[4], MsgTypeUrls: []string{url}}
_, err = srv.TripCircuitBreaker(ft.Ctx, unknownTrip)
require.Error(t, err)
// user has permission to trip circuit breaker for two messages but only has permission for one
url, url2 = "cosmos.staking.v1beta1.MsgCreateValidator", "cosmos.staking.v1beta1.MsgEditValidator"
somemsgs := &types.Permissions{Level: types.Permissions_LEVEL_SOME_MSGS, LimitTypeUrls: []string{url}}
msg = &types.MsgAuthorizeCircuitBreaker{Granter: addresses[0], Grantee: addresses[2], Permissions: somemsgs}
_, err = srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.NoError(t, err)
// try to trip two messages but user only has permission for one
someTrip := &types.MsgTripCircuitBreaker{Authority: addresses[2], MsgTypeUrls: []string{url, url2}}
_, err = srv.TripCircuitBreaker(ft.Ctx, someTrip)
require.ErrorContains(t, err, "MsgEditValidator")
// user tries to trip an already tripped circuit breaker
alreadyTripped := "cosmos.bank.v1beta1.MsgSend"
twoTrip := &types.MsgTripCircuitBreaker{Authority: addresses[1], MsgTypeUrls: []string{alreadyTripped}}
_, err = srv.TripCircuitBreaker(ft.Ctx, twoTrip)
require.Error(t, err)
}
func Test_ResetCircuitBreaker(t *testing.T) {
ft := setupFixture(t)
srv := msgServer{
Keeper: ft.Keeper,
}
// admin resets circuit breaker
url := "cosmos.bank.v1beta1.MsgSend"
// admin trips circuit breaker
admintrip := &types.MsgTripCircuitBreaker{Authority: addresses[0], MsgTypeUrls: []string{url}}
_, err := srv.TripCircuitBreaker(ft.Ctx, admintrip)
require.NoError(t, err)
allowed := ft.Keeper.IsAllowed(ft.Ctx, url)
require.False(t, allowed, "circuit breaker should be tripped")
adminReset := &types.MsgResetCircuitBreaker{Authority: addresses[0], MsgTypeUrls: []string{url}}
_, err = srv.ResetCircuitBreaker(ft.Ctx, adminReset)
require.NoError(t, err)
allowed = ft.Keeper.IsAllowed(ft.Ctx, url)
require.True(t, allowed, "circuit breaker should be reset")
// user has no permission to reset circuit breaker
// admin trips circuit breaker
_, err = srv.TripCircuitBreaker(ft.Ctx, admintrip)
require.NoError(t, err)
allowed = ft.Keeper.IsAllowed(ft.Ctx, url)
require.False(t, allowed, "circuit breaker should be tripped")
unknownUserReset := &types.MsgResetCircuitBreaker{Authority: addresses[1], MsgTypeUrls: []string{url}}
_, err = srv.ResetCircuitBreaker(ft.Ctx, unknownUserReset)
require.Error(t, err)
allowed = ft.Keeper.IsAllowed(ft.Ctx, url)
require.False(t, allowed, "circuit breaker should be reset")
// user with all messages resets circuit breaker
allmsgs := &types.Permissions{Level: types.Permissions_LEVEL_ALL_MSGS, LimitTypeUrls: []string{""}}
msg := &types.MsgAuthorizeCircuitBreaker{Granter: addresses[0], Grantee: addresses[1], Permissions: allmsgs}
_, err = srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.NoError(t, err)
// trip the circuit breaker
url2 := "cosmos.staking.v1beta1.MsgDelegate"
admintrip = &types.MsgTripCircuitBreaker{Authority: addresses[0], MsgTypeUrls: []string{url2}}
_, err = srv.TripCircuitBreaker(ft.Ctx, admintrip)
require.NoError(t, err)
// user with all messages resets circuit breaker
allMsgsReset := &types.MsgResetCircuitBreaker{Authority: addresses[1], MsgTypeUrls: []string{url}}
_, err = srv.ResetCircuitBreaker(ft.Ctx, allMsgsReset)
require.NoError(t, err)
// user tries to reset an message they dont have permission to reset
url = "cosmos.staking.v1beta1.MsgCreateValidator"
// give restricted perms to a user
someMsgs := &types.Permissions{Level: types.Permissions_LEVEL_SOME_MSGS, LimitTypeUrls: []string{url2}}
msg = &types.MsgAuthorizeCircuitBreaker{Granter: addresses[0], Grantee: addresses[2], Permissions: someMsgs}
_, err = srv.AuthorizeCircuitBreaker(ft.Ctx, msg)
require.NoError(t, err)
admintrip = &types.MsgTripCircuitBreaker{Authority: addresses[0], MsgTypeUrls: []string{url}}
_, err = srv.TripCircuitBreaker(ft.Ctx, admintrip)
require.NoError(t, err)
// user with all messages resets circuit breaker
someMsgsReset := &types.MsgResetCircuitBreaker{Authority: addresses[2], MsgTypeUrls: []string{url}}
_, err = srv.ResetCircuitBreaker(ft.Ctx, someMsgsReset)
require.NoError(t, err)
// user tries to reset an already reset circuit breaker
admintrip = &types.MsgTripCircuitBreaker{Authority: addresses[1], MsgTypeUrls: []string{url2}}
_, err = srv.TripCircuitBreaker(ft.Ctx, admintrip)
require.Error(t, err)
}

View File

@ -1,45 +0,0 @@
package keeper
import (
"testing"
"github.com/regen-network/gocuke"
"gotest.tools/v3/assert"
)
func TestTrip(t *testing.T) {
t.Skip("TODO: uncomment this after implementing")
gocuke.NewRunner(t, &tripSuite{}).Path("../features/msg_trip.feature").Run()
}
type tripSuite struct {
*baseFixture
}
func (s *tripSuite) Before(t *testing.T) {
s.baseFixture = initFixture(t)
}
func (s *tripSuite) HasPermission(a, b string) {
panic("PENDING")
}
func (s *tripSuite) HasNoPermissions(a string) {
panic("PENDING")
}
func (s *tripSuite) AttemptsToTripCircuit(a, b string, c gocuke.DocString) {
panic("PENDING")
}
func (s *tripSuite) ExpectSuccess() {
assert.NilError(s.t, s.err)
}
func (s *tripSuite) ExpectAnError(a string) {
assert.Error(s.t, s.err, a)
}
func (s *tripSuite) ExpectThatHasNoPermissions(a string) {
panic("PENDING")
}

90
x/circuit/keeper/query.go Normal file
View File

@ -0,0 +1,90 @@
package keeper
import (
"bytes"
"context"
"cosmossdk.io/store/prefix"
"cosmossdk.io/x/circuit/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/cosmos/gogoproto/proto"
)
var _ types.QueryServer = QueryServer{}
type QueryServer struct {
keeper Keeper
}
// NewMsgServerImpl returns an implementation of the bank MsgServer interface
// for the provided Keeper.
func NewQueryServer(keeper Keeper) types.QueryServer {
return &QueryServer{keeper: keeper}
}
// Account returns account permissions.
func (qs QueryServer) Account(c context.Context, req *types.QueryAccountRequest) (*types.AccountResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(c)
add, err := qs.keeper.addressCodec.StringToBytes(req.Address)
if err != nil {
return nil, err
}
perms, err := qs.keeper.GetPermissions(sdkCtx, add)
if err != nil {
return nil, err
}
return &types.AccountResponse{Permission: perms}, nil
}
// Account returns account permissions.
func (qs QueryServer) Accounts(c context.Context, req *types.QueryAccountsRequest) (*types.AccountsResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(c)
// Iterate over accounts and perform the callback
var accounts []*types.GenesisAccountPermissions
store := sdkCtx.KVStore(qs.keeper.storekey)
accountsStore := prefix.NewStore(store, types.AccountPermissionPrefix)
pageRes, err := query.Paginate(accountsStore, req.Pagination, func(key, value []byte) error {
perm := &types.Permissions{}
if err := proto.Unmarshal(value, perm); err != nil {
return err
}
// remove key suffix
address, err := qs.keeper.addressCodec.BytesToString(bytes.TrimRight(key, "\x00"))
if err != nil {
return err
}
accounts = append(accounts, &types.GenesisAccountPermissions{
Address: address,
Permissions: perm,
})
return nil
})
if err != nil {
return nil, err
}
return &types.AccountsResponse{Accounts: accounts, Pagination: pageRes}, nil
}
// DisabledList returns a list of disabled message urls
func (qs QueryServer) DisabledList(c context.Context, req *types.QueryDisabledListRequest) (*types.DisabledListResponse, error) {
sdkCtx := sdk.UnwrapSDKContext(c)
// Iterate over disabled list and perform the callback
var msgs []string
qs.keeper.IterateDisableLists(sdkCtx, func(address []byte, perm types.Permissions) (stop bool) {
msgs = append(msgs, perm.LimitTypeUrls...)
return false
})
return &types.DisabledListResponse{DisabledList: msgs}, nil
}

View File

@ -0,0 +1,77 @@
package keeper
import (
"testing"
"cosmossdk.io/x/circuit/types"
"github.com/cosmos/cosmos-sdk/types/query"
"github.com/stretchr/testify/require"
)
func TestQueryAccount(t *testing.T) {
t.Parallel()
f := setupFixture(t)
add, err := f.Keeper.addressCodec.StringToBytes(addresses[0])
require.NoError(t, err)
err = f.Keeper.SetPermissions(f.Ctx, add, &f.MockPerms)
require.NoError(t, err)
// create a new query server
qs := QueryServer{keeper: f.Keeper}
// test the Account method
res, err := qs.Account(f.Ctx, &types.QueryAccountRequest{Address: addresses[0]})
require.NoError(t, err)
require.Equal(t, res.Permission.Level, types.Permissions_Level(3))
require.Equal(t, res.Permission.LimitTypeUrls, []string{
"test",
})
}
func TestQueryAccounts(t *testing.T) {
t.Parallel()
f := setupFixture(t)
add, err := f.Keeper.addressCodec.StringToBytes(addresses[0])
require.NoError(t, err)
err = f.Keeper.SetPermissions(f.Ctx, add, &f.MockPerms)
require.NoError(t, err)
// create a new query server
qs := QueryServer{keeper: f.Keeper}
// test the Accounts method
res1, err := qs.Accounts(f.Ctx, &types.QueryAccountsRequest{
Pagination: &query.PageRequest{Limit: 10},
})
require.NoError(t, err)
for _, a := range res1.Accounts {
require.Equal(t, addresses[0], a.Address)
require.Equal(t, f.MockPerms, *a.Permissions)
}
require.NotNil(t, res1)
}
func TestQueryDisabledList(t *testing.T) {
t.Parallel()
f := setupFixture(t)
add, err := f.Keeper.addressCodec.StringToBytes(addresses[0])
require.NoError(t, err)
err = f.Keeper.SetPermissions(f.Ctx, add, &f.MockPerms)
require.NoError(t, err)
// create a new query server
qs := QueryServer{keeper: f.Keeper}
// test the DisabledList method
disabledList, err := qs.DisabledList(f.Ctx, &types.QueryDisabledListRequest{})
require.NoError(t, err)
require.Equal(t, []string{"test"}, disabledList.DisabledList)
}

View File

@ -2,23 +2,46 @@ package keeper
import (
"testing"
storetypes "cosmossdk.io/store/types"
cmproto "github.com/cometbft/cometbft/proto/tendermint/types"
addresscodec "github.com/cosmos/cosmos-sdk/codec/address"
"github.com/cosmos/cosmos-sdk/testutil"
sdk "github.com/cosmos/cosmos-sdk/types"
"cosmossdk.io/x/circuit/types"
)
type baseFixture struct {
t *testing.T
err error
// TODO: uncomment these after implementing.
// ctx context.Context
// k Keeper
// addrs []sdk.AccAddress
// storeKey *storetypes.KVStoreKey
// sdkCtx sdk.Context
var addresses = []string{
"cosmos1zglwfu6xjzvzagqcmvzewyzjp9xwqw5qwrr8n9",
"cosmos1p8s0p6gqc6c9gt77lgr2qqujz49huhu6a80smx",
"cosmos1qasf9ehx8m7cnat39ndc74rx3fg7z66u8lw0fd",
"cosmos1uxrdj5zfuudhypsmmjxnj4gpu432ycht06a05a",
"cosmos1wn7k8a7fwpmrwnm94ndj0germfnxnhl6hs8spj",
}
func initFixture(t *testing.T) *baseFixture {
s := &baseFixture{t: t}
return s
type fixture struct {
Ctx sdk.Context
Keeper Keeper
MockPerms types.Permissions
MockMsgURL string
}
func setupFixture(t *testing.T) *fixture {
mockStoreKey := storetypes.NewKVStoreKey("circuit")
keeperX := NewKeeper(mockStoreKey, addresses[0], addresscodec.NewBech32Codec("cosmos"))
mockMsgURL := "mock_url"
mockCtx := testutil.DefaultContextWithDB(t, mockStoreKey, storetypes.NewTransientStoreKey("transient_test"))
ctx := mockCtx.Ctx.WithBlockHeader(cmproto.Header{})
mockPerms := types.Permissions{
Level: 3,
LimitTypeUrls: []string{"test"},
}
return &fixture{
Ctx: ctx,
Keeper: keeperX,
MockPerms: mockPerms,
MockMsgURL: mockMsgURL,
}
}

184
x/circuit/module.go Normal file
View File

@ -0,0 +1,184 @@
package circuit
import (
"context"
"encoding/json"
"fmt"
"time"
modulev1 "cosmossdk.io/api/cosmos/circuit/module/v1"
"cosmossdk.io/core/address"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/depinject"
store "cosmossdk.io/store/types"
"cosmossdk.io/x/circuit/client/cli"
abci "github.com/cometbft/cometbft/abci/types"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
codectypes "github.com/cosmos/cosmos-sdk/codec/types"
"github.com/cosmos/cosmos-sdk/telemetry"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/module"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
gwruntime "github.com/grpc-ecosystem/grpc-gateway/runtime"
"github.com/spf13/cobra"
"cosmossdk.io/x/circuit/keeper"
"cosmossdk.io/x/circuit/types"
)
// ConsensusVersion defines the current circuit module consensus version.
const ConsensusVersion = 1
var (
_ module.AppModuleGenesis = AppModule{}
_ module.AppModuleBasic = AppModuleBasic{}
)
// AppModuleBasic defines the basic application module used by the circuit module.
type AppModuleBasic struct {
cdc codec.Codec
}
// Name returns the circuit module's name.
func (AppModuleBasic) Name() string { return types.ModuleName }
// RegisterLegacyAminoCodec registers the circuit module's types on the LegacyAmino codec.
func (AppModuleBasic) RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
types.RegisterLegacyAminoCodec(cdc)
}
// DefaultGenesis returns default genesis state as raw bytes for the circuit
// module.
func (AppModuleBasic) DefaultGenesis(cdc codec.JSONCodec) json.RawMessage {
return cdc.MustMarshalJSON(types.DefaultGenesisState())
}
// ValidateGenesis performs genesis state validation for the circuit module.
func (AppModuleBasic) ValidateGenesis(cdc codec.JSONCodec, _ client.TxEncodingConfig, bz json.RawMessage) error {
var data types.GenesisState
if err := cdc.UnmarshalJSON(bz, &data); err != nil {
return fmt.Errorf("failed to unmarshal %s genesis state: %w", types.ModuleName, err)
}
return data.Validate()
}
// RegisterGRPCGatewayRoutes registers the gRPC Gateway routes for the circuit module.
func (AppModuleBasic) RegisterGRPCGatewayRoutes(clientCtx client.Context, mux *gwruntime.ServeMux) {
if err := types.RegisterQueryHandlerClient(context.Background(), mux, types.NewQueryClient(clientCtx)); err != nil {
panic(err)
}
}
// GetTxCmd returns the root tx command for the circuit module.
func (AppModuleBasic) GetTxCmd() *cobra.Command {
return cli.NewTxCmd()
}
// GetQueryCmd returns no root query command for the circuit module.
func (AppModuleBasic) GetQueryCmd() *cobra.Command {
return cli.GetQueryCmd()
}
// RegisterInterfaces registers interfaces and implementations of the circuit module.
func (AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry) {
types.RegisterInterfaces(registry)
}
// AppModule implements an application module for the circuit module.
type AppModule struct {
AppModuleBasic
keeper keeper.Keeper
}
var _ appmodule.AppModule = AppModule{}
// IsOnePerModuleType implements the depinject.OnePerModuleType interface.
func (am AppModule) IsOnePerModuleType() {}
// IsAppModule implements the appmodule.AppModule interface.
func (am AppModule) IsAppModule() {}
// RegisterServices registers module services.
func (am AppModule) RegisterServices(cfg module.Configurator) {
types.RegisterMsgServer(cfg.MsgServer(), keeper.NewMsgServerImpl(am.keeper))
types.RegisterQueryServer(cfg.QueryServer(), keeper.NewQueryServer(am.keeper))
}
// NewAppModule creates a new AppModule object
func NewAppModule(cdc codec.Codec, keeper keeper.Keeper) AppModule {
return AppModule{
AppModuleBasic: AppModuleBasic{cdc: cdc},
keeper: keeper,
}
}
// Name returns the circuit module's name.
func (AppModule) Name() string { return types.ModuleName }
// RegisterInvariants registers the circuit module invariants.
func (am AppModule) RegisterInvariants(ir sdk.InvariantRegistry) {
}
// ConsensusVersion implements AppModule/ConsensusVersion.
func (AppModule) ConsensusVersion() uint64 { return ConsensusVersion }
func (am AppModule) InitGenesis(ctx sdk.Context, cdc codec.JSONCodec, data json.RawMessage) []abci.ValidatorUpdate {
start := time.Now()
var genesisState types.GenesisState
cdc.MustUnmarshalJSON(data, &genesisState)
telemetry.MeasureSince(start, "InitGenesis", "crisis", "unmarshal")
am.keeper.InitGenesis(ctx, &genesisState)
return []abci.ValidatorUpdate{}
}
// ExportGenesis returns the exported genesis state as raw bytes for the circuit
// module.
func (am AppModule) ExportGenesis(ctx sdk.Context, cdc codec.JSONCodec) json.RawMessage {
gs := am.keeper.ExportGenesis(ctx)
return cdc.MustMarshalJSON(gs)
}
func init() {
appmodule.Register(
&modulev1.Module{},
appmodule.Provide(ProvideModule),
)
}
type Inputs struct {
depinject.In
Config *modulev1.Module
Cdc codec.Codec
Key *store.KVStoreKey
AddressCodec address.Codec
}
type Outputs struct {
depinject.Out
CircuitKeeper keeper.Keeper
Module appmodule.AppModule
}
func ProvideModule(in Inputs) Outputs {
// default to governance authority if not provided
authority := authtypes.NewModuleAddress("gov")
if in.Config.Authority != "" {
authority = authtypes.NewModuleAddressOrBech32Address(in.Config.Authority)
}
circuitkeeper := keeper.NewKeeper(
in.Key,
authority.String(),
in.AddressCodec,
)
m := NewAppModule(in.Cdc, circuitkeeper)
return Outputs{CircuitKeeper: circuitkeeper, Module: m}
}

48
x/circuit/types/codec.go Normal file
View File

@ -0,0 +1,48 @@
package types
import (
"github.com/cosmos/cosmos-sdk/codec"
"github.com/cosmos/cosmos-sdk/codec/legacy"
types "github.com/cosmos/cosmos-sdk/codec/types"
cryptocodec "github.com/cosmos/cosmos-sdk/crypto/codec"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/msgservice"
authzcodec "github.com/cosmos/cosmos-sdk/x/authz/codec"
govcodec "github.com/cosmos/cosmos-sdk/x/gov/codec"
groupcodec "github.com/cosmos/cosmos-sdk/x/group/codec"
)
var (
amino = codec.NewLegacyAmino()
ModuleCdc = codec.NewAminoCodec(amino)
)
func init() {
RegisterLegacyAminoCodec(amino)
cryptocodec.RegisterCrypto(amino)
sdk.RegisterLegacyAminoCodec(amino)
// Register all Amino interfaces and concrete types on the authz and gov Amino codec so that this can later be
// used to properly serialize MsgGrant, MsgExec and MsgSubmitProposal instances
RegisterLegacyAminoCodec(authzcodec.Amino)
RegisterLegacyAminoCodec(govcodec.Amino)
RegisterLegacyAminoCodec(groupcodec.Amino)
}
// RegisterLegacyAminoCodec registers the necessary x/bank interfaces and concrete types
// on the provided LegacyAmino codec. These types are used for Amino JSON serialization.
func RegisterLegacyAminoCodec(cdc *codec.LegacyAmino) {
legacy.RegisterAminoMsg(cdc, &MsgAuthorizeCircuitBreaker{}, "cosmos-sdk/MsgAuthorizeCircuitBreaker")
legacy.RegisterAminoMsg(cdc, &MsgResetCircuitBreaker{}, "cosmos-sdk/MsgResetCircuitBreaker")
legacy.RegisterAminoMsg(cdc, &MsgTripCircuitBreaker{}, "cosmos-sdk/MsgTripCircuitBreaker")
}
// RegisterInterfaces registers the interfaces types with the interface registry.
func RegisterInterfaces(registry types.InterfaceRegistry) {
registry.RegisterImplementations((*sdk.Msg)(nil),
&MsgAuthorizeCircuitBreaker{},
&MsgResetCircuitBreaker{},
&MsgTripCircuitBreaker{},
)
msgservice.RegisterMsgServiceDesc(registry, &_Msg_serviceDesc)
}

View File

@ -0,0 +1,31 @@
package types
import "fmt"
func DefaultGenesisState() *GenesisState {
return &GenesisState{}
}
func (gs *GenesisState) Validate() error {
for _, account := range gs.AccountPermissions {
if account.Address == "" {
return fmt.Errorf("invalid account address: %s", account.Address)
}
if account.Permissions == nil {
return fmt.Errorf("account has empty permissions, account address: %s", account.Address)
}
if err := CheckPermission(account); err != nil {
return err
}
}
return nil
}
func CheckPermission(account *GenesisAccountPermissions) error {
if account.Permissions.Level != Permissions_LEVEL_ALL_MSGS && account.Permissions.Level != Permissions_LEVEL_SOME_MSGS && account.Permissions.Level != Permissions_LEVEL_SUPER_ADMIN {
return fmt.Errorf("invalid permission level account address: %s, permission level: %s", account.Address, account.Permissions.Level)
}
return nil
}

32
x/circuit/types/keys.go Normal file
View File

@ -0,0 +1,32 @@
package types
const (
// ModuleName defines the module name
ModuleName = "circuit"
// StoreKey defines the primary module store key
StoreKey = ModuleName
// RouterKey defines the module's message routing key
RouterKey = ModuleName
)
// KVStore keys
var (
AccountPermissionPrefix = []byte{0x01}
DisableListPrefix = []byte{0x02}
)
func CreateAddressPrefix(account []byte) []byte {
key := make([]byte, len(AccountPermissionPrefix)+len(account)+1)
copy(key, AccountPermissionPrefix)
copy(key[len(AccountPermissionPrefix):], account)
return key
}
func CreateDisableMsgPrefix(msgURL string) []byte {
key := make([]byte, len(DisableListPrefix)+len(msgURL)+1)
copy(key, DisableListPrefix)
copy(key[len(DisableListPrefix):], msgURL)
return key
}

90
x/circuit/types/msgs.go Normal file
View File

@ -0,0 +1,90 @@
package types
import (
sdk "github.com/cosmos/cosmos-sdk/types"
)
var (
_ sdk.Msg = &MsgAuthorizeCircuitBreaker{}
_ sdk.Msg = &MsgTripCircuitBreaker{}
_ sdk.Msg = &MsgResetCircuitBreaker{}
)
// NewMsgAuthorizeCircuitBreaker creates a new MsgAuthorizeCircuitBreaker instance.
func NewMsgAuthorizeCircuitBreaker(granter, grantee string, permission *Permissions) *MsgAuthorizeCircuitBreaker {
return &MsgAuthorizeCircuitBreaker{
Granter: granter,
Grantee: grantee,
Permissions: permission,
}
}
// Route Implements Msg.
func (m MsgAuthorizeCircuitBreaker) Route() string { return sdk.MsgTypeURL(&m) }
// Type Implements Msg.
func (m MsgAuthorizeCircuitBreaker) Type() string { return sdk.MsgTypeURL(&m) }
// GetSignBytes Implements Msg.
func (m MsgAuthorizeCircuitBreaker) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
}
// GetSigners returns the expected signers for a MsgAuthorizeCircuitBreaker.
func (m MsgAuthorizeCircuitBreaker) GetSigners() []sdk.AccAddress {
granter := sdk.MustAccAddressFromBech32(m.Granter)
return []sdk.AccAddress{granter}
}
// NewMsgTripCircuitBreaker creates a new MsgTripCircuitBreaker instance.
func NewMsgTripCircuitBreaker(authority string, urls []string) *MsgTripCircuitBreaker {
return &MsgTripCircuitBreaker{
Authority: authority,
MsgTypeUrls: urls,
}
}
// Route Implements Msg.
func (m MsgTripCircuitBreaker) Route() string { return sdk.MsgTypeURL(&m) }
// Type Implements Msg.
func (m MsgTripCircuitBreaker) Type() string { return sdk.MsgTypeURL(&m) }
// GetSignBytes Implements Msg.
func (m MsgTripCircuitBreaker) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
}
// GetSigners returns the expected signers for a MsgTripCircuitBreaker.
func (m MsgTripCircuitBreaker) GetSigners() []sdk.AccAddress {
granter := sdk.MustAccAddressFromBech32(m.Authority)
return []sdk.AccAddress{granter}
}
// NewMsgResetCircuitBreaker creates a new MsgResetCircuitBreaker instance.
func NewMsgResetCircuitBreaker(authority string, urls []string) *MsgResetCircuitBreaker {
return &MsgResetCircuitBreaker{
Authority: authority,
MsgTypeUrls: urls,
}
}
// Route Implements Msg.
func (m MsgResetCircuitBreaker) Route() string { return sdk.MsgTypeURL(&m) }
// Type Implements Msg.
func (m MsgResetCircuitBreaker) Type() string { return sdk.MsgTypeURL(&m) }
// GetSignBytes Implements Msg.
func (m MsgResetCircuitBreaker) GetSignBytes() []byte {
return sdk.MustSortJSON(ModuleCdc.MustMarshalJSON(&m))
}
// GetSigners returns the expected signers for a MsgResetCircuitBreaker.
func (m MsgResetCircuitBreaker) GetSigners() []sdk.AccAddress {
granter := sdk.MustAccAddressFromBech32(m.Authority)
return []sdk.AccAddress{granter}
}

View File

@ -6,7 +6,6 @@ package types
import (
context "context"
fmt "fmt"
_ "github.com/cosmos/cosmos-sdk/types/msgservice"
query "github.com/cosmos/cosmos-sdk/types/query"
grpc1 "github.com/cosmos/gogoproto/grpc"
proto "github.com/cosmos/gogoproto/proto"
@ -314,41 +313,40 @@ func init() {
func init() { proto.RegisterFile("cosmos/circuit/v1/query.proto", fileDescriptor_87c65073a3d3c1e1) }
var fileDescriptor_87c65073a3d3c1e1 = []byte{
// 531 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xcf, 0x6b, 0x13, 0x41,
0x14, 0xc7, 0x33, 0x2d, 0xb5, 0xed, 0x6b, 0x45, 0x1d, 0x0b, 0x86, 0x6d, 0xbb, 0xd6, 0x2d, 0x36,
0xa1, 0x96, 0x1d, 0x12, 0xc1, 0x8b, 0x20, 0x28, 0x62, 0x3d, 0x78, 0x68, 0x73, 0xf4, 0xa0, 0x4c,
0x76, 0x87, 0x65, 0x30, 0xd9, 0xd9, 0xee, 0xdb, 0x04, 0x8b, 0x08, 0xd2, 0x93, 0xde, 0x44, 0xff,
0x06, 0xef, 0x1e, 0xfc, 0x23, 0x3c, 0x16, 0xbc, 0x78, 0x94, 0x44, 0xf0, 0xdf, 0x90, 0xec, 0xce,
0x6c, 0x36, 0x76, 0x6a, 0x8f, 0x33, 0xef, 0x7d, 0xbf, 0xf3, 0x79, 0x3f, 0x76, 0x61, 0x33, 0x50,
0xd8, 0x57, 0xc8, 0x02, 0x99, 0x06, 0x03, 0x99, 0xb1, 0x61, 0x8b, 0x1d, 0x0d, 0x44, 0x7a, 0xec,
0x27, 0xa9, 0xca, 0x14, 0xbd, 0x56, 0x84, 0x7d, 0x1d, 0xf6, 0x87, 0x2d, 0x67, 0x57, 0x2b, 0xba,
0x1c, 0x45, 0x91, 0xcb, 0x86, 0xad, 0xae, 0xc8, 0x78, 0x8b, 0x25, 0x3c, 0x92, 0x31, 0xcf, 0xa4,
0x8a, 0x0b, 0xb9, 0x73, 0x43, 0xe7, 0xf6, 0x31, 0x9a, 0x38, 0xf7, 0x31, 0xd2, 0x01, 0xcb, 0xb3,
0xd9, 0x71, 0x22, 0x50, 0x87, 0x37, 0x22, 0xa5, 0xa2, 0x9e, 0x60, 0x3c, 0x91, 0x8c, 0xc7, 0xb1,
0xca, 0x72, 0x53, 0x13, 0x5d, 0xd7, 0x62, 0xf3, 0x78, 0x95, 0xd8, 0x63, 0x70, 0xfd, 0x70, 0x72,
0x7c, 0x18, 0x04, 0x6a, 0x10, 0x67, 0x1d, 0x71, 0x34, 0x10, 0x98, 0xd1, 0x3a, 0x2c, 0xf2, 0x30,
0x4c, 0x05, 0x62, 0x9d, 0x6c, 0x91, 0xe6, 0x72, 0xc7, 0x1c, 0xbd, 0x43, 0xb8, 0x52, 0xe6, 0x62,
0xa2, 0x62, 0x14, 0xf4, 0x01, 0x40, 0x22, 0xd2, 0xbe, 0x44, 0x94, 0x2a, 0xce, 0xf3, 0x57, 0xda,
0xae, 0x7f, 0xa6, 0x15, 0xfe, 0x41, 0x99, 0x84, 0x9d, 0x8a, 0xc2, 0x7b, 0x01, 0x6b, 0x55, 0x06,
0x34, 0x10, 0x4f, 0x00, 0xa6, 0x2d, 0xd2, 0xbe, 0x3b, 0xc6, 0x77, 0xd2, 0x4f, 0xbf, 0xa8, 0x44,
0xf7, 0xd3, 0x3f, 0xe0, 0x91, 0xd0, 0xda, 0x4e, 0x45, 0xe9, 0x7d, 0x21, 0x70, 0x75, 0xea, 0xad,
0xa1, 0x9f, 0xc2, 0x12, 0xd7, 0x77, 0x75, 0xb2, 0x35, 0xdf, 0x5c, 0x69, 0xef, 0x59, 0x90, 0xf7,
0x45, 0x2c, 0x50, 0xa2, 0x56, 0x57, 0x0b, 0x28, 0xd5, 0x74, 0x7f, 0x06, 0x73, 0x2e, 0xc7, 0x6c,
0x5c, 0x88, 0x59, 0x60, 0xcc, 0x70, 0x3a, 0x50, 0xcf, 0xfb, 0xf0, 0x58, 0x22, 0xef, 0xf6, 0x44,
0xf8, 0x4c, 0xa2, 0x19, 0x88, 0x77, 0x1f, 0xd6, 0x66, 0xaf, 0x75, 0x19, 0xdb, 0x70, 0x39, 0xd4,
0xf7, 0x2f, 0x7b, 0x12, 0xb3, 0xbc, 0x96, 0xe5, 0xce, 0x6a, 0x58, 0x49, 0x6e, 0x7f, 0x9b, 0x87,
0x85, 0xdc, 0x99, 0x7e, 0x20, 0xb0, 0xa8, 0x8b, 0xa1, 0x3b, 0x96, 0x7a, 0x2d, 0xbb, 0xe0, 0x78,
0x96, 0xbc, 0x7f, 0x56, 0xc0, 0x6b, 0xbf, 0xff, 0xf3, 0x75, 0x97, 0x9c, 0xfc, 0xf8, 0xfd, 0x79,
0xae, 0x41, 0x6f, 0xb3, 0xb3, 0xeb, 0x6a, 0xba, 0xc5, 0xde, 0xe8, 0x45, 0x7a, 0x4b, 0x4f, 0x08,
0x2c, 0x99, 0xb1, 0xd0, 0xc6, 0x05, 0x30, 0x66, 0x29, 0x9c, 0xed, 0xf3, 0x69, 0xca, 0xe1, 0x7a,
0xcd, 0x29, 0xce, 0x26, 0x5d, 0xff, 0x0f, 0x0e, 0xfd, 0x44, 0x60, 0xb5, 0xda, 0x58, 0x7a, 0xe7,
0x3c, 0x10, 0xcb, 0x54, 0x1c, 0x1b, 0xb5, 0x6d, 0x4c, 0xde, 0xde, 0x14, 0xe8, 0x16, 0xbd, 0x69,
0x01, 0xd2, 0xf3, 0xca, 0x67, 0xe8, 0x2c, 0xbc, 0x9b, 0x64, 0x3f, 0xba, 0xf7, 0x7d, 0xe4, 0x92,
0xd3, 0x91, 0x4b, 0x7e, 0x8d, 0x5c, 0xf2, 0x71, 0xec, 0xd6, 0x4e, 0xc7, 0x6e, 0xed, 0xe7, 0xd8,
0xad, 0x3d, 0xdf, 0x28, 0x0c, 0x30, 0x7c, 0xe5, 0x4b, 0xc5, 0x5e, 0x97, 0x46, 0xf9, 0x4f, 0xa1,
0x7b, 0x29, 0xff, 0xb4, 0xef, 0xfe, 0x0d, 0x00, 0x00, 0xff, 0xff, 0xab, 0xa8, 0x92, 0x5a, 0xad,
0x04, 0x00, 0x00,
// 516 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x84, 0x94, 0xc1, 0x6b, 0x13, 0x4f,
0x14, 0xc7, 0x33, 0x2d, 0xbf, 0x5f, 0xdb, 0xd7, 0x8a, 0x3a, 0xf6, 0x10, 0xb6, 0xed, 0x5a, 0xb7,
0xd8, 0x84, 0x5a, 0x76, 0x48, 0x04, 0x2f, 0x82, 0xa0, 0x88, 0xf5, 0xe0, 0xa1, 0xdd, 0xa3, 0x07,
0x65, 0xb2, 0x3b, 0x84, 0xc1, 0x74, 0x67, 0xbb, 0x6f, 0x13, 0x2c, 0xe2, 0xa5, 0x27, 0xbd, 0x89,
0xfe, 0x0d, 0x1e, 0x05, 0xff, 0x0c, 0x8f, 0x05, 0x2f, 0x1e, 0x25, 0x11, 0xfc, 0x37, 0xa4, 0xb3,
0x33, 0x9b, 0x8d, 0x9d, 0xda, 0xe3, 0xcc, 0xbc, 0xef, 0xcb, 0xe7, 0xfb, 0xbe, 0x2f, 0x0b, 0x1b,
0xb1, 0xc2, 0x43, 0x85, 0x2c, 0x96, 0x79, 0x3c, 0x94, 0x05, 0x1b, 0x75, 0xd8, 0xd1, 0x50, 0xe4,
0xc7, 0x61, 0x96, 0xab, 0x42, 0xd1, 0xeb, 0xe5, 0x73, 0x68, 0x9e, 0xc3, 0x51, 0xc7, 0xdb, 0x31,
0x8a, 0x1e, 0x47, 0x51, 0xd6, 0xb2, 0x51, 0xa7, 0x27, 0x0a, 0xde, 0x61, 0x19, 0xef, 0xcb, 0x94,
0x17, 0x52, 0xa5, 0xa5, 0xdc, 0x73, 0x74, 0x2f, 0x8e, 0x33, 0x81, 0xe6, 0x79, 0xbd, 0xaf, 0x54,
0x7f, 0x20, 0x18, 0xcf, 0x24, 0xe3, 0x69, 0xaa, 0x0a, 0xad, 0xb5, 0xaf, 0x6b, 0x46, 0x6c, 0x7f,
0xa3, 0x0e, 0x16, 0x30, 0xb8, 0x71, 0x70, 0x76, 0x7c, 0x18, 0xc7, 0x6a, 0x98, 0x16, 0x91, 0x38,
0x1a, 0x0a, 0x2c, 0x68, 0x13, 0x16, 0x78, 0x92, 0xe4, 0x02, 0xb1, 0x49, 0x36, 0x49, 0x7b, 0x29,
0xb2, 0xc7, 0xe0, 0x00, 0xae, 0x56, 0xb5, 0x98, 0xa9, 0x14, 0x05, 0x7d, 0x00, 0x90, 0x89, 0xfc,
0x50, 0x22, 0x4a, 0x95, 0xea, 0xfa, 0xe5, 0xae, 0x1f, 0x9e, 0x73, 0x1c, 0xee, 0x57, 0x45, 0x18,
0xd5, 0x14, 0xc1, 0x0b, 0x58, 0xad, 0x33, 0xa0, 0x85, 0x78, 0x02, 0x30, 0x9d, 0x84, 0xe9, 0xbb,
0x6d, 0xfb, 0x9e, 0x8d, 0x2d, 0x2c, 0x9d, 0x98, 0xb1, 0x85, 0xfb, 0xbc, 0x2f, 0x8c, 0x36, 0xaa,
0x29, 0x83, 0xcf, 0x04, 0xae, 0x4d, 0x7b, 0x1b, 0xe8, 0xa7, 0xb0, 0xc8, 0xcd, 0x5d, 0x93, 0x6c,
0xce, 0xb7, 0x97, 0xbb, 0xbb, 0x0e, 0xe4, 0x3d, 0x91, 0x0a, 0x94, 0x68, 0xd4, 0x75, 0x03, 0x95,
0x9a, 0xee, 0xcd, 0x60, 0xce, 0x69, 0xcc, 0xd6, 0xa5, 0x98, 0x25, 0xc6, 0x0c, 0xa7, 0x07, 0x4d,
0x3d, 0x87, 0xc7, 0x12, 0x79, 0x6f, 0x20, 0x92, 0x67, 0x12, 0x6d, 0x20, 0xc1, 0x7d, 0x58, 0x9d,
0xbd, 0x36, 0x36, 0xb6, 0xe0, 0x4a, 0x62, 0xee, 0x5f, 0x0e, 0x24, 0x16, 0xda, 0xcb, 0x52, 0xb4,
0x92, 0xd4, 0x8a, 0xbb, 0x5f, 0xe6, 0xe1, 0x3f, 0xdd, 0x99, 0xbe, 0x27, 0xb0, 0x60, 0xcc, 0xd0,
0x6d, 0x87, 0x5f, 0xc7, 0x2e, 0x78, 0x81, 0xa3, 0xee, 0xaf, 0x15, 0x08, 0xba, 0xef, 0x7e, 0x7f,
0xdd, 0x21, 0x27, 0xdf, 0x7f, 0x7d, 0x9a, 0x6b, 0xd1, 0xdb, 0xec, 0xfc, 0xba, 0xda, 0x69, 0xb1,
0x37, 0x66, 0x91, 0xde, 0xd2, 0x13, 0x02, 0x8b, 0x36, 0x16, 0xda, 0xba, 0x04, 0xc6, 0x2e, 0x85,
0xb7, 0x75, 0x31, 0x4d, 0x15, 0x6e, 0xd0, 0x9e, 0xe2, 0x6c, 0xd0, 0xb5, 0x7f, 0xe0, 0xd0, 0x8f,
0x04, 0x56, 0xea, 0x83, 0xa5, 0x77, 0x2e, 0x02, 0x71, 0xa4, 0xe2, 0xb9, 0xa8, 0x5d, 0x31, 0x05,
0xbb, 0x53, 0xa0, 0x5b, 0xf4, 0xa6, 0x03, 0xc8, 0xe4, 0xa5, 0x33, 0x7c, 0x74, 0xef, 0xdb, 0xd8,
0x27, 0xa7, 0x63, 0x9f, 0xfc, 0x1c, 0xfb, 0xe4, 0xc3, 0xc4, 0x6f, 0x9c, 0x4e, 0xfc, 0xc6, 0x8f,
0x89, 0xdf, 0x78, 0xbe, 0x5e, 0x2a, 0x31, 0x79, 0x15, 0x4a, 0xc5, 0x5e, 0x57, 0x1d, 0xf4, 0xd7,
0xa0, 0xf7, 0xbf, 0xfe, 0x4f, 0xdf, 0xfd, 0x13, 0x00, 0x00, 0xff, 0xff, 0xd7, 0xdd, 0xdb, 0x03,
0x8d, 0x04, 0x00, 0x00,
}
// Reference imports to suppress errors if they are not otherwise used.