mirror of
https://github.com/eosswedenorg/thalos
synced 2026-07-02 11:43:40 +02:00
Merge branch 'blackwhitelist' into dev
This commit is contained in:
commit
18918faad0
9 changed files with 192 additions and 75 deletions
|
|
@ -238,6 +238,8 @@ func GetConfig(flags *pflag.FlagSet) (*config.Config, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cfg.Ship.Blacklist.SetWhitelist(cfg.Ship.BlacklistIsWhitelist)
|
||||||
|
|
||||||
return cfg, nil
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,8 @@ ship:
|
||||||
# blacklist all action from a contract
|
# blacklist all action from a contract
|
||||||
# evilcontract: ["*"]
|
# evilcontract: ["*"]
|
||||||
|
|
||||||
|
# blacklist_is_whitelist: true
|
||||||
|
|
||||||
# Telegram notifications
|
# Telegram notifications
|
||||||
#telegram:
|
#telegram:
|
||||||
# id: "123456789:GPdmGPBWvpgHPxlergJLavus-PoAURTjMWP"
|
# id: "123456789:GPdmGPBWvpgHPxlergJLavus-PoAURTjMWP"
|
||||||
|
|
|
||||||
|
|
@ -53,6 +53,7 @@ func NewBuilder() *Builder {
|
||||||
"ship.max_messages_in_flight": "max-msg-in-flight",
|
"ship.max_messages_in_flight": "max-msg-in-flight",
|
||||||
"ship.chain": "chain",
|
"ship.chain": "chain",
|
||||||
"ship.blacklist": "blacklist",
|
"ship.blacklist": "blacklist",
|
||||||
|
"ship.blacklist_is_whitelist": "blacklist-is-whitelist",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -116,30 +117,9 @@ func (b *Builder) Build() (*Config, error) {
|
||||||
mapstructure.StringToTimeDurationHookFunc(),
|
mapstructure.StringToTimeDurationHookFunc(),
|
||||||
mapstructure.StringToSliceHookFunc(","),
|
mapstructure.StringToSliceHookFunc(","),
|
||||||
func(f reflect.Type, t reflect.Type, in interface{}) (interface{}, error) {
|
func(f reflect.Type, t reflect.Type, in interface{}) (interface{}, error) {
|
||||||
if t == reflect.TypeOf(types.Blacklist{}) && f.Kind() == reflect.Slice {
|
if t == reflect.TypeOf(types.Blacklist{}) {
|
||||||
if v, ok := in.([]string); ok {
|
return decodeIntoBlacklist(in)
|
||||||
list := types.Blacklist{}
|
|
||||||
for _, i := range v {
|
|
||||||
var action string
|
|
||||||
parts := strings.SplitN(i, ":", 2)
|
|
||||||
|
|
||||||
if len(parts) < 2 {
|
|
||||||
action = "*"
|
|
||||||
} else {
|
|
||||||
action = parts[1]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
list.Add(parts[0], action)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(list) < 1 {
|
|
||||||
list = nil
|
|
||||||
}
|
|
||||||
return list, nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("Must be a string slice")
|
|
||||||
}
|
|
||||||
|
|
||||||
return in, nil
|
return in, nil
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
@ -151,3 +131,61 @@ func (b *Builder) Build() (*Config, error) {
|
||||||
|
|
||||||
return &conf, nil
|
return &conf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Decode a generic structure into types.Blacklist
|
||||||
|
func decodeIntoBlacklist(in any) (*types.Blacklist, error) {
|
||||||
|
switch v := in.(type) {
|
||||||
|
// Standard map structure.
|
||||||
|
case map[string]any:
|
||||||
|
return blacklistParseMap(v)
|
||||||
|
|
||||||
|
// slice of "contract:action" pairs. Usually from CLI
|
||||||
|
case []string:
|
||||||
|
return blacklistParseSlice(v)
|
||||||
|
|
||||||
|
// Sometimes we have a slice of interfaces.
|
||||||
|
// Need to convert it to a slice of strings.
|
||||||
|
case []any:
|
||||||
|
sv := make([]string, len(v))
|
||||||
|
for i, j := range v {
|
||||||
|
sv[i] = j.(string)
|
||||||
|
}
|
||||||
|
return blacklistParseSlice(sv)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("Must be a string slice")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blacklist map parser
|
||||||
|
func blacklistParseMap(in map[string]any) (*types.Blacklist, error) {
|
||||||
|
list := &types.Blacklist{}
|
||||||
|
for k, v := range in {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case []any:
|
||||||
|
for _, v := range v {
|
||||||
|
list.Add(k, v.(string))
|
||||||
|
}
|
||||||
|
case any:
|
||||||
|
list.Add(k, v.(string))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blacklist slice parser
|
||||||
|
func blacklistParseSlice(in []string) (*types.Blacklist, error) {
|
||||||
|
list := &types.Blacklist{}
|
||||||
|
for _, i := range in {
|
||||||
|
var action string
|
||||||
|
parts := strings.SplitN(i, ":", 2)
|
||||||
|
|
||||||
|
if len(parts) < 2 {
|
||||||
|
action = "*"
|
||||||
|
} else {
|
||||||
|
action = parts[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
list.Add(parts[0], action)
|
||||||
|
}
|
||||||
|
return list, nil
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,10 +28,11 @@ func TestBuilder(t *testing.T) {
|
||||||
EndBlockNum: 23872222,
|
EndBlockNum: 23872222,
|
||||||
IrreversibleOnly: true,
|
IrreversibleOnly: true,
|
||||||
MaxMessagesInFlight: 1337,
|
MaxMessagesInFlight: 1337,
|
||||||
Blacklist: types.Blacklist{
|
Blacklist: *types.NewBlacklist(map[string][]string{
|
||||||
"eosio": {"noop"},
|
"eosio": {"noop"},
|
||||||
"contract": {"skip1", "skip2"},
|
"contract": {"skip1", "skip2"},
|
||||||
},
|
}),
|
||||||
|
BlacklistIsWhitelist: true,
|
||||||
},
|
},
|
||||||
Telegram: TelegramConfig{
|
Telegram: TelegramConfig{
|
||||||
Id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw",
|
Id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw",
|
||||||
|
|
@ -64,10 +65,11 @@ ship:
|
||||||
start_block_num: 23671836
|
start_block_num: 23671836
|
||||||
end_block_num: 23872222
|
end_block_num: 23872222
|
||||||
blacklist:
|
blacklist:
|
||||||
eosio: ["noop"]
|
eosio: noop
|
||||||
contract:
|
contract:
|
||||||
- skip1
|
- skip1
|
||||||
- skip2
|
- skip2
|
||||||
|
blacklist_is_whitelist: true
|
||||||
telegram:
|
telegram:
|
||||||
id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw"
|
id: "110201543:AAHdqTcvCH1vGWJxfSeofSAs0K5PALDsaw"
|
||||||
channel: -123456789
|
channel: -123456789
|
||||||
|
|
@ -186,6 +188,7 @@ func TestBuilder_Flags(t *testing.T) {
|
||||||
require.NoError(t, flags.Set("max-msg-in-flight", "98"))
|
require.NoError(t, flags.Set("max-msg-in-flight", "98"))
|
||||||
require.NoError(t, flags.Set("chain", "wax"))
|
require.NoError(t, flags.Set("chain", "wax"))
|
||||||
require.NoError(t, flags.Set("blacklist", "contract:action1,contract:action2,contract2:action1"))
|
require.NoError(t, flags.Set("blacklist", "contract:action1,contract:action2,contract2:action1"))
|
||||||
|
require.NoError(t, flags.Set("blacklist-is-whitelist", "true"))
|
||||||
|
|
||||||
cfg, err := NewBuilder().
|
cfg, err := NewBuilder().
|
||||||
SetSource(bytes.NewReader([]byte(``))).
|
SetSource(bytes.NewReader([]byte(``))).
|
||||||
|
|
@ -207,10 +210,11 @@ func TestBuilder_Flags(t *testing.T) {
|
||||||
MaxMessagesInFlight: 98,
|
MaxMessagesInFlight: 98,
|
||||||
IrreversibleOnly: true,
|
IrreversibleOnly: true,
|
||||||
Chain: "wax",
|
Chain: "wax",
|
||||||
Blacklist: types.Blacklist{
|
Blacklist: *types.NewBlacklist(map[string][]string{
|
||||||
"contract": {"action1", "action2"},
|
"contract": {"action1", "action2"},
|
||||||
"contract2": {"action1"},
|
"contract2": {"action1"},
|
||||||
},
|
}),
|
||||||
|
BlacklistIsWhitelist: true,
|
||||||
},
|
},
|
||||||
Telegram: TelegramConfig{
|
Telegram: TelegramConfig{
|
||||||
Id: "72983126312982618",
|
Id: "72983126312982618",
|
||||||
|
|
@ -229,20 +233,28 @@ func TestBuilder_Flags(t *testing.T) {
|
||||||
require.Equal(t, &expected, cfg)
|
require.Equal(t, &expected, cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBuilder_BlacklistFlag(t *testing.T) {
|
func TestBuilder_BlacklistSlice(t *testing.T) {
|
||||||
flags := GetFlags()
|
expected := Config{
|
||||||
|
Ship: ShipConfig{
|
||||||
require.NoError(t, flags.Set("blacklist", "contract,contract:action2"))
|
Blacklist: *types.NewBlacklist(map[string][]string{
|
||||||
|
"contract": {"action"},
|
||||||
conf, err := NewBuilder().
|
"contract2": {"action2"},
|
||||||
SetSource(bytes.NewReader([]byte(``))).
|
"contract3": {"*"},
|
||||||
SetFlags(flags).
|
}),
|
||||||
Build()
|
},
|
||||||
|
|
||||||
expected := types.Blacklist{
|
|
||||||
"contract": {"*", "action2"},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
builder := NewBuilder()
|
||||||
|
builder.SetSource(bytes.NewBuffer([]byte(`
|
||||||
|
ship:
|
||||||
|
blacklist:
|
||||||
|
- "contract:action"
|
||||||
|
- "contract2:action2"
|
||||||
|
- contract3
|
||||||
|
`)))
|
||||||
|
|
||||||
|
cfg, err := builder.Build()
|
||||||
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.Equal(t, expected, conf.Ship.Blacklist)
|
require.Equal(t, &expected, cfg)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,7 @@ func GetFlags() *pflag.FlagSet {
|
||||||
flags.String("chain", "", "ChainID used in channel namespace, can be any string (default from api)")
|
flags.String("chain", "", "ChainID used in channel namespace, can be any string (default from api)")
|
||||||
|
|
||||||
flags.StringSlice("blacklist", []string{}, "Define a list of 'contract:action' pairs that will be blacklisted (thalos will not process those actions)")
|
flags.StringSlice("blacklist", []string{}, "Define a list of 'contract:action' pairs that will be blacklisted (thalos will not process those actions)")
|
||||||
|
flags.Bool("blacklist-is-whitelist", false, "Thalos will treat the blacklist as a whitelist")
|
||||||
|
|
||||||
return &flags
|
return &flags
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ type ShipConfig struct {
|
||||||
EndBlockNum uint32 `yaml:"end_block_num" mapstructure:"end_block_num"`
|
EndBlockNum uint32 `yaml:"end_block_num" mapstructure:"end_block_num"`
|
||||||
Chain string `yaml:"chain" mapstructure:"chain"`
|
Chain string `yaml:"chain" mapstructure:"chain"`
|
||||||
Blacklist types.Blacklist `yaml:"blacklist" mapstructure:"blacklist"`
|
Blacklist types.Blacklist `yaml:"blacklist" mapstructure:"blacklist"`
|
||||||
|
BlacklistIsWhitelist bool `yaml:"blacklist_is_whitelist" mapstructure:"blacklist_is_whitelist"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
|
|
|
||||||
|
|
@ -153,7 +153,7 @@ func (processor *ShipProcessor) proccessActionTrace(logger *log.Entry, trace *sh
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check blacklist if we should skip this action
|
// Check blacklist if we should skip this action
|
||||||
if processor.blacklist.Lookup(trace.Act.Account.String(), trace.Act.Name.String()) {
|
if !processor.blacklist.IsAllowed(trace.Act.Account.String(), trace.Act.Name.String()) {
|
||||||
logger.WithFields(log.Fields{
|
logger.WithFields(log.Fields{
|
||||||
"contract": trace.Act.Account,
|
"contract": trace.Act.Account,
|
||||||
"action": trace.Act.Name,
|
"action": trace.Act.Name,
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,47 @@
|
||||||
package types
|
package types
|
||||||
|
|
||||||
type Blacklist map[string][]string
|
type Blacklist struct {
|
||||||
|
table map[string][]string
|
||||||
func (bl Blacklist) Add(contract string, action string) {
|
isWhitelist bool
|
||||||
if len(bl[contract]) < 1 {
|
|
||||||
bl[contract] = []string{}
|
|
||||||
}
|
|
||||||
bl[contract] = append(bl[contract], action)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (bl Blacklist) Lookup(contract string, action string) bool {
|
func NewBlacklist(entries map[string][]string) *Blacklist {
|
||||||
if v, ok := bl[contract]; ok {
|
return &Blacklist{
|
||||||
|
table: entries,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bl *Blacklist) SetWhitelist(value bool) *Blacklist {
|
||||||
|
bl.isWhitelist = value
|
||||||
|
return bl
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bl Blacklist) Empty() bool {
|
||||||
|
return len(bl.table) < 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bl *Blacklist) Add(contract string, action string) {
|
||||||
|
if bl.table == nil {
|
||||||
|
bl.table = map[string][]string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(bl.table[contract]) < 1 {
|
||||||
|
bl.table[contract] = []string{}
|
||||||
|
}
|
||||||
|
bl.table[contract] = append(bl.table[contract], action)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bl Blacklist) IsAllowed(contract string, action string) bool {
|
||||||
|
if v, ok := bl.table[contract]; ok {
|
||||||
for _, act := range v {
|
for _, act := range v {
|
||||||
if act == action || act == "*" {
|
if act == action || act == "*" {
|
||||||
return true
|
return bl.isWhitelist == true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return bl.isWhitelist == false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (bl Blacklist) IsDenied(contract string, action string) bool {
|
||||||
|
return bl.IsAllowed(contract, action)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,38 +6,73 @@ import (
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestBlacklist_Empty(t *testing.T) {
|
||||||
|
bl := Blacklist{
|
||||||
|
table: map[string][]string{},
|
||||||
|
}
|
||||||
|
|
||||||
|
require.True(t, bl.Empty())
|
||||||
|
|
||||||
|
bl.Add("contract", "action1")
|
||||||
|
|
||||||
|
require.False(t, bl.Empty())
|
||||||
|
}
|
||||||
|
|
||||||
func TestBlacklist_Add(t *testing.T) {
|
func TestBlacklist_Add(t *testing.T) {
|
||||||
bl := Blacklist{}
|
bl := Blacklist{
|
||||||
|
table: map[string][]string{},
|
||||||
|
}
|
||||||
bl.Add("contract", "action1")
|
bl.Add("contract", "action1")
|
||||||
bl.Add("contract", "action2")
|
bl.Add("contract", "action2")
|
||||||
bl.Add("contract2", "action1")
|
bl.Add("contract2", "action1")
|
||||||
|
|
||||||
expected := Blacklist{
|
expected := Blacklist{
|
||||||
|
table: map[string][]string{
|
||||||
"contract": {"action1", "action2"},
|
"contract": {"action1", "action2"},
|
||||||
"contract2": {"action1"},
|
"contract2": {"action1"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
require.Equal(t, expected, bl)
|
require.Equal(t, expected, bl)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlacklist_Lookup(t *testing.T) {
|
func TestBlacklist_IsAllowed(t *testing.T) {
|
||||||
bl := Blacklist{
|
bl := Blacklist{
|
||||||
|
table: map[string][]string{
|
||||||
"mycontract": {"myaction", "noop"},
|
"mycontract": {"myaction", "noop"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
require.True(t, bl.Lookup("mycontract", "myaction"))
|
require.False(t, bl.IsAllowed("mycontract", "myaction"))
|
||||||
require.True(t, bl.Lookup("mycontract", "noop"))
|
require.False(t, bl.IsAllowed("mycontract", "noop"))
|
||||||
require.False(t, bl.Lookup("mycontract", "xxx"))
|
require.True(t, bl.IsAllowed("mycontract", "xxx"))
|
||||||
require.False(t, bl.Lookup("xxx", "yyy"))
|
require.True(t, bl.IsAllowed("xxx", "yyy"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBlacklist_LookupWildcard(t *testing.T) {
|
func TestBlacklist_IsAllowedWildcard(t *testing.T) {
|
||||||
bl := Blacklist{
|
bl := Blacklist{
|
||||||
|
table: map[string][]string{
|
||||||
"mycontract": {"*"},
|
"mycontract": {"*"},
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
require.True(t, bl.Lookup("mycontract", "myaction"))
|
require.False(t, bl.IsAllowed("mycontract", "myaction"))
|
||||||
require.True(t, bl.Lookup("mycontract", "noop"))
|
require.False(t, bl.IsAllowed("mycontract", "noop"))
|
||||||
require.True(t, bl.Lookup("mycontract", "xxx"))
|
require.False(t, bl.IsAllowed("mycontract", "xxx"))
|
||||||
require.False(t, bl.Lookup("xxx", "yyy"))
|
require.True(t, bl.IsAllowed("xxx", "yyy"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestBlacklist_Whitelist(t *testing.T) {
|
||||||
|
bl := Blacklist{
|
||||||
|
table: map[string][]string{
|
||||||
|
"mycontract": {"myaction", "noop"},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
bl.SetWhitelist(true)
|
||||||
|
|
||||||
|
require.True(t, bl.IsAllowed("mycontract", "myaction"))
|
||||||
|
require.True(t, bl.IsAllowed("mycontract", "noop"))
|
||||||
|
require.False(t, bl.IsAllowed("mycontract", "xxx"))
|
||||||
|
require.False(t, bl.IsAllowed("xxx", "yyy"))
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue