feat: fallback rpc endpoint

This commit is contained in:
Anthony Laibe 2023-02-20 10:32:45 +01:00 committed by Anthony Laibe
parent f77bff6d25
commit 60e1978bb5
37 changed files with 2705 additions and 307 deletions

View File

@ -1 +1 @@
0.133.2
0.133.3

View File

@ -48,6 +48,7 @@
// 1676368933_keypairs_to_keycards.up.sql (639B)
// 1676951398_add_currency_format_cache.up.sql (291B)
// 1676968196_keycards_add_clock_column.up.sql (73B)
// 1676968197_add_fallback_rpc_to_networks.up.sql (112B)
// doc.go (74B)
package migrations
@ -132,7 +133,7 @@ func _1640111208_dummyUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1640111208_dummy.up.sql", size: 258, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1640111208_dummy.up.sql", size: 258, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0xf0, 0xae, 0x20, 0x6e, 0x75, 0xd1, 0x36, 0x14, 0xf2, 0x40, 0xe5, 0xd6, 0x7a, 0xc4, 0xa5, 0x72, 0xaa, 0xb5, 0x4d, 0x71, 0x97, 0xb8, 0xe8, 0x95, 0x22, 0x95, 0xa2, 0xac, 0xaf, 0x48, 0x58}}
return a, nil
}
@ -152,7 +153,7 @@ func _1642666031_add_removed_clock_to_bookmarksUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1642666031_add_removed_clock_to_bookmarks.up.sql", size: 117, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1642666031_add_removed_clock_to_bookmarks.up.sql", size: 117, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x84, 0x4e, 0x38, 0x99, 0x7a, 0xc, 0x90, 0x13, 0xec, 0xfe, 0x2f, 0x55, 0xff, 0xb7, 0xb6, 0xaa, 0x96, 0xc6, 0x92, 0x79, 0xcc, 0xee, 0x4e, 0x99, 0x53, 0xfe, 0x1c, 0xbb, 0x32, 0x2, 0xa4, 0x27}}
return a, nil
}
@ -172,7 +173,7 @@ func _1643644541_gif_api_key_settingUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1643644541_gif_api_key_setting.up.sql", size: 108, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1643644541_gif_api_key_setting.up.sql", size: 108, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1b, 0x94, 0x28, 0xfb, 0x66, 0xd1, 0x7c, 0xb8, 0x89, 0xe2, 0xb4, 0x71, 0x65, 0x24, 0x57, 0x22, 0x95, 0x38, 0x97, 0x3, 0x9b, 0xc6, 0xa4, 0x41, 0x7b, 0xba, 0xf7, 0xdb, 0x70, 0xf7, 0x20, 0x3a}}
return a, nil
}
@ -192,7 +193,7 @@ func _1644188994_recent_stickersUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1644188994_recent_stickers.up.sql", size: 79, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1644188994_recent_stickers.up.sql", size: 79, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1e, 0xad, 0xaa, 0x30, 0xbf, 0x4, 0x7, 0xf8, 0xc3, 0x3, 0xb8, 0x97, 0x23, 0x2b, 0xbd, 0x1c, 0x60, 0x69, 0xb0, 0x42, 0x5e, 0x6b, 0xd, 0xa7, 0xa3, 0x6b, 0x2e, 0xdc, 0x70, 0x13, 0x72, 0x7}}
return a, nil
}
@ -212,7 +213,7 @@ func _1646659233_add_address_to_dapp_permisssionUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1646659233_add_address_to_dapp_permisssion.up.sql", size: 700, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1646659233_add_address_to_dapp_permisssion.up.sql", size: 700, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xed, 0xb0, 0x35, 0xcc, 0x2e, 0x16, 0xe6, 0x15, 0x86, 0x2c, 0x37, 0x80, 0xae, 0xa3, 0xc5, 0x31, 0x78, 0x5, 0x9d, 0xcd, 0x7b, 0xeb, 0x5f, 0xf2, 0xb3, 0x74, 0x72, 0xdf, 0xcf, 0x88, 0xb, 0x40}}
return a, nil
}
@ -232,7 +233,7 @@ func _1646841105_add_emoji_accountUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1646841105_add_emoji_account.up.sql", size: 96, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1646841105_add_emoji_account.up.sql", size: 96, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe6, 0x77, 0x29, 0x95, 0x18, 0x64, 0x82, 0x63, 0xe7, 0xaf, 0x6c, 0xa9, 0x15, 0x7d, 0x46, 0xa6, 0xbc, 0xdf, 0xa7, 0xd, 0x2b, 0xd2, 0x2d, 0x97, 0x4d, 0xa, 0x6b, 0xd, 0x6e, 0x90, 0x42, 0x5c}}
return a, nil
}
@ -252,7 +253,7 @@ func _1647278782_display_nameUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647278782_display_name.up.sql", size: 110, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1647278782_display_name.up.sql", size: 110, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf4, 0xa1, 0x1f, 0x3e, 0x61, 0x65, 0x8d, 0xff, 0xee, 0xde, 0xc5, 0x91, 0xd9, 0x5c, 0xb5, 0xe2, 0xf0, 0xb7, 0xe7, 0x5c, 0x5c, 0x16, 0x25, 0x89, 0xee, 0x78, 0x12, 0xea, 0x3e, 0x48, 0x41, 0xa6}}
return a, nil
}
@ -272,7 +273,7 @@ func _1647862838_reset_last_backupUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647862838_reset_last_backup.up.sql", size: 37, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1647862838_reset_last_backup.up.sql", size: 37, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x21, 0xe3, 0xd5, 0xf6, 0x5f, 0xfe, 0x65, 0xfa, 0x1d, 0x88, 0xf8, 0x5f, 0x24, 0x71, 0x34, 0x68, 0x96, 0x2a, 0x60, 0x87, 0x15, 0x82, 0x4d, 0x8a, 0x59, 0x3d, 0x1f, 0xd8, 0x56, 0xd4, 0xfb, 0xda}}
return a, nil
}
@ -292,7 +293,7 @@ func _1647871652_add_settings_sync_clock_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647871652_add_settings_sync_clock_table.up.sql", size: 1044, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1647871652_add_settings_sync_clock_table.up.sql", size: 1044, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd8, 0x58, 0xec, 0x85, 0x90, 0xfa, 0x30, 0x98, 0x98, 0x9a, 0xa6, 0xa8, 0x96, 0x2b, 0x38, 0x93, 0xf3, 0xae, 0x46, 0x74, 0xa4, 0x41, 0x62, 0x9b, 0x2, 0x86, 0xbf, 0xe5, 0x2a, 0xce, 0xe2, 0xc0}}
return a, nil
}
@ -312,7 +313,7 @@ func _1647880168_add_torrent_configUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647880168_add_torrent_config.up.sql", size: 211, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1647880168_add_torrent_config.up.sql", size: 211, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1, 0x92, 0x22, 0x37, 0x96, 0xf3, 0xb5, 0x5b, 0x27, 0xd0, 0x7d, 0x43, 0x5, 0x4e, 0x9d, 0xe2, 0x49, 0xbe, 0x86, 0x31, 0xa1, 0x89, 0xff, 0xd6, 0x51, 0xe0, 0x9c, 0xb, 0xda, 0xfc, 0xf2, 0x93}}
return a, nil
}
@ -332,7 +333,7 @@ func _1647882837_add_communities_settings_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647882837_add_communities_settings_table.up.sql", size: 206, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1647882837_add_communities_settings_table.up.sql", size: 206, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xbd, 0x87, 0x78, 0x99, 0xd9, 0x5d, 0xbd, 0xf7, 0x57, 0x9c, 0xca, 0x97, 0xbd, 0xb3, 0xe9, 0xb5, 0x89, 0x31, 0x3f, 0xf6, 0x5c, 0x13, 0xb, 0xc3, 0x54, 0x93, 0x18, 0x40, 0x7, 0x82, 0xfe, 0x7e}}
return a, nil
}
@ -352,7 +353,7 @@ func _1647956635_add_waku_messages_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1647956635_add_waku_messages_table.up.sql", size: 266, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1647956635_add_waku_messages_table.up.sql", size: 266, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0xe, 0xe1, 0xdc, 0xda, 0x2e, 0x89, 0x8d, 0xdc, 0x2a, 0x1c, 0x13, 0xa1, 0xfc, 0xfe, 0xf, 0xb2, 0xb9, 0x85, 0xc8, 0x45, 0xd6, 0xd1, 0x7, 0x5c, 0xa3, 0x8, 0x47, 0x44, 0x6d, 0x96, 0xe0}}
return a, nil
}
@ -372,7 +373,7 @@ func _1648554928_network_testUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1648554928_network_test.up.sql", size: 132, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1648554928_network_test.up.sql", size: 132, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x9a, 0xc5, 0x7f, 0x87, 0xf3, 0x2c, 0xf7, 0xbb, 0xd3, 0x3a, 0x4e, 0x76, 0x88, 0xca, 0xaf, 0x73, 0xce, 0x8f, 0xa1, 0xf6, 0x3d, 0x4d, 0xed, 0x6f, 0x49, 0xf2, 0xfe, 0x56, 0x2a, 0x60, 0x68, 0xca}}
return a, nil
}
@ -392,7 +393,7 @@ func _1649174829_add_visitble_tokenUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1649174829_add_visitble_token.up.sql", size: 84, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1649174829_add_visitble_token.up.sql", size: 84, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xa3, 0x22, 0xc0, 0x2b, 0x3f, 0x4f, 0x3d, 0x5e, 0x4c, 0x68, 0x7c, 0xd0, 0x15, 0x36, 0x9f, 0xec, 0xa1, 0x2a, 0x7b, 0xb4, 0xe3, 0xc6, 0xc9, 0xb4, 0x81, 0x50, 0x4a, 0x11, 0x3b, 0x35, 0x7, 0xcf}}
return a, nil
}
@ -412,7 +413,7 @@ func _1649882262_add_derived_from_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1649882262_add_derived_from_accounts.up.sql", size: 110, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1649882262_add_derived_from_accounts.up.sql", size: 110, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x11, 0xb9, 0x44, 0x4d, 0x85, 0x8d, 0x7f, 0xb4, 0xae, 0x4f, 0x5c, 0x66, 0x64, 0xb6, 0xe2, 0xe, 0x3d, 0xad, 0x9d, 0x8, 0x4f, 0xab, 0x6e, 0xa8, 0x7d, 0x76, 0x3, 0xad, 0x96, 0x1, 0xee, 0x5c}}
return a, nil
}
@ -432,7 +433,7 @@ func _1650612625_add_community_message_archive_hashes_tableUpSql() (*asset, erro
return nil, err
}
info := bindataFileInfo{name: "1650612625_add_community_message_archive_hashes_table.up.sql", size: 130, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1650612625_add_community_message_archive_hashes_table.up.sql", size: 130, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x48, 0x31, 0xb3, 0x75, 0x23, 0xe2, 0x45, 0xe, 0x47, 0x1b, 0x35, 0xa5, 0x6e, 0x83, 0x4e, 0x64, 0x7d, 0xd7, 0xa2, 0xda, 0xe9, 0x53, 0xf1, 0x16, 0x86, 0x2c, 0x57, 0xad, 0xfa, 0xca, 0x39, 0xde}}
return a, nil
}
@ -452,7 +453,7 @@ func _1650616788_add_communities_archives_info_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1650616788_add_communities_archives_info_table.up.sql", size: 208, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1650616788_add_communities_archives_info_table.up.sql", size: 208, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd1, 0x4f, 0x80, 0x45, 0xb9, 0xd9, 0x15, 0xe2, 0x78, 0xd0, 0xcb, 0x71, 0xc1, 0x1b, 0xb7, 0x1b, 0x1b, 0x97, 0xfe, 0x47, 0x53, 0x3c, 0x62, 0xbc, 0xdd, 0x3a, 0x94, 0x1a, 0xc, 0x48, 0x76, 0xe}}
return a, nil
}
@ -472,7 +473,7 @@ func _1652715604_add_clock_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1652715604_add_clock_accounts.up.sql", size: 62, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1652715604_add_clock_accounts.up.sql", size: 62, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb6, 0xd9, 0x8d, 0x73, 0xc9, 0xef, 0xfa, 0xb1, 0x4b, 0xa5, 0xf3, 0x5, 0x19, 0x26, 0x46, 0xf8, 0x47, 0x93, 0xdb, 0xac, 0x2, 0xef, 0xf9, 0x71, 0x56, 0x83, 0xe6, 0x2d, 0xb0, 0xd7, 0x83, 0x5c}}
return a, nil
}
@ -492,7 +493,7 @@ func _1653037334_add_notifications_settings_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1653037334_add_notifications_settings_table.up.sql", size: 1276, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1653037334_add_notifications_settings_table.up.sql", size: 1276, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4b, 0xc4, 0x65, 0xac, 0xa, 0xf2, 0xef, 0xb6, 0x39, 0x3c, 0xc5, 0xb1, 0xb2, 0x9c, 0x86, 0x58, 0xe0, 0x38, 0xcb, 0x57, 0x3c, 0x76, 0x73, 0x87, 0x79, 0x4e, 0xf6, 0xed, 0xb0, 0x8e, 0x9e, 0xa}}
return a, nil
}
@ -512,7 +513,7 @@ func _1654702119_add_mutual_contact_settingsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1654702119_add_mutual_contact_settings.up.sql", size: 78, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1654702119_add_mutual_contact_settings.up.sql", size: 78, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x26, 0x66, 0x67, 0x50, 0xfe, 0xd7, 0xe3, 0x29, 0x8b, 0xff, 0x9d, 0x5a, 0x87, 0xa7, 0x99, 0x6e, 0xd6, 0xcd, 0x2e, 0xbb, 0x17, 0xdf, 0x7f, 0xf7, 0xa3, 0xfa, 0x32, 0x7c, 0x2d, 0x92, 0xc8, 0x74}}
return a, nil
}
@ -532,7 +533,7 @@ func _1655375270_add_clock_field_to_communities_settings_tableUpSql() (*asset, e
return nil, err
}
info := bindataFileInfo{name: "1655375270_add_clock_field_to_communities_settings_table.up.sql", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1655375270_add_clock_field_to_communities_settings_table.up.sql", size: 74, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x19, 0xc5, 0xc0, 0xf9, 0x84, 0x53, 0xdf, 0x83, 0xcf, 0xb6, 0x40, 0x6d, 0xf5, 0xdc, 0x77, 0x37, 0xb7, 0xe3, 0xa, 0x75, 0xe7, 0x6, 0x11, 0xca, 0x2b, 0x51, 0x92, 0xdd, 0x7d, 0xdb, 0xc3, 0xf5}}
return a, nil
}
@ -552,7 +553,7 @@ func _1655385721_drop_networks_configUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1655385721_drop_networks_config.up.sql", size: 27, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1655385721_drop_networks_config.up.sql", size: 27, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfc, 0xa7, 0x20, 0xbb, 0x67, 0x21, 0xe, 0xc6, 0xc8, 0x21, 0x74, 0xe0, 0xce, 0xc8, 0xe2, 0x2, 0xb4, 0xea, 0xf0, 0xe5, 0xc4, 0x4d, 0xdd, 0xd4, 0x52, 0x31, 0xa9, 0x3d, 0xcd, 0xd8, 0x9b, 0xab}}
return a, nil
}
@ -572,7 +573,7 @@ func _1655385724_networks_chaincolor_shortnameUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1655385724_networks_chainColor_shortName.up.sql", size: 220, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1655385724_networks_chainColor_shortName.up.sql", size: 220, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xd9, 0xe7, 0x84, 0xbb, 0x5f, 0xd2, 0x2c, 0x42, 0x88, 0x62, 0x52, 0xb6, 0x58, 0x31, 0xac, 0xc, 0x96, 0x2b, 0x1b, 0xe5, 0x4e, 0x9a, 0x3a, 0xf6, 0xf6, 0xfc, 0xa9, 0x1a, 0x35, 0x62, 0x28, 0x88}}
return a, nil
}
@ -592,7 +593,7 @@ func _1655456688_add_deleted_at_field_to_bookmarks_tableUpSql() (*asset, error)
return nil, err
}
info := bindataFileInfo{name: "1655456688_add_deleted_at_field_to_bookmarks_table.up.sql", size: 69, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1655456688_add_deleted_at_field_to_bookmarks_table.up.sql", size: 69, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xe7, 0x9a, 0xbd, 0x9a, 0xc9, 0xf, 0xdf, 0x90, 0x0, 0x5d, 0xea, 0x6e, 0x7d, 0x51, 0x95, 0xcd, 0x90, 0xd3, 0x1a, 0x36, 0x6c, 0xf4, 0xbd, 0xa7, 0x6b, 0xbf, 0xe5, 0xdb, 0xa3, 0x88, 0xe3, 0x50}}
return a, nil
}
@ -612,7 +613,7 @@ func _1655462032_create_bookmarks_deleted_at_indexUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1655462032_create_bookmarks_deleted_at_index.up.sql", size: 81, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1655462032_create_bookmarks_deleted_at_index.up.sql", size: 81, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf, 0x8e, 0x20, 0x6b, 0x14, 0x9e, 0xcd, 0x97, 0xd3, 0xfe, 0x62, 0x3, 0x26, 0x59, 0x1, 0x6c, 0x99, 0xef, 0x6d, 0x21, 0xd4, 0xb5, 0xa3, 0xf4, 0x39, 0x40, 0x54, 0x6, 0xd, 0x60, 0x13, 0x38}}
return a, nil
}
@ -632,7 +633,7 @@ func _1657617291_add_multi_transactions_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1657617291_add_multi_transactions_table.up.sql", size: 412, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1657617291_add_multi_transactions_table.up.sql", size: 412, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x86, 0xb0, 0x4e, 0x8c, 0x4, 0x82, 0xb4, 0x43, 0xaa, 0xd0, 0x16, 0xdd, 0xcb, 0x88, 0x81, 0xac, 0x4, 0x34, 0x1a, 0x8f, 0x2e, 0xc5, 0x69, 0xb, 0xf0, 0x17, 0xf7, 0xe3, 0x9, 0xe, 0x54, 0xe0}}
return a, nil
}
@ -652,7 +653,7 @@ func _1660134042_add_social_links_settings_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1660134042_add_social_links_settings_table.up.sql", size: 334, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1660134042_add_social_links_settings_table.up.sql", size: 334, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x84, 0x73, 0xb6, 0xe7, 0x3f, 0xaa, 0x39, 0x9a, 0x56, 0x56, 0x31, 0xf1, 0x8e, 0x26, 0x23, 0x1, 0xe4, 0xfa, 0x98, 0xfe, 0x78, 0x87, 0x20, 0xcb, 0x52, 0xf4, 0x38, 0x7f, 0xc4, 0x1c, 0x4, 0x22}}
return a, nil
}
@ -672,7 +673,7 @@ func _1660134060_settings_bioUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1660134060_settings_bio.up.sql", size: 91, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1660134060_settings_bio.up.sql", size: 91, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x46, 0x25, 0xa0, 0xa6, 0x47, 0xff, 0xbc, 0x2a, 0x0, 0xff, 0x59, 0x4b, 0xb0, 0xc9, 0x4e, 0x15, 0xe4, 0xd9, 0xda, 0xeb, 0xfe, 0x55, 0x98, 0xc3, 0x9d, 0x96, 0xe7, 0xf, 0xd1, 0x5c, 0x93, 0x73}}
return a, nil
}
@ -692,7 +693,7 @@ func _1660134070_add_wakuv2_storeUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1660134070_add_wakuv2_store.up.sql", size: 269, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1660134070_add_wakuv2_store.up.sql", size: 269, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x1d, 0xe6, 0xc3, 0x9, 0xef, 0xdc, 0xae, 0x49, 0x30, 0x78, 0x54, 0xd6, 0xdb, 0xbf, 0xc0, 0x8e, 0x25, 0x8f, 0xfc, 0x67, 0x80, 0x39, 0x37, 0xd4, 0x86, 0xc1, 0x85, 0xc8, 0x99, 0xc4, 0x59, 0xd4}}
return a, nil
}
@ -712,7 +713,7 @@ func _1660134072_waku2_store_messagesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1660134072_waku2_store_messages.up.sql", size: 497, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1660134072_waku2_store_messages.up.sql", size: 497, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x3e, 0xeb, 0xb4, 0xa0, 0xa1, 0x2b, 0xcb, 0x4c, 0x3c, 0xc6, 0xd0, 0xe8, 0x96, 0xe3, 0x96, 0xf1, 0x4f, 0x1f, 0xe0, 0xe7, 0x1f, 0x85, 0xa3, 0xe, 0xf7, 0x52, 0x56, 0x63, 0x2b, 0xb0, 0x87, 0x7b}}
return a, nil
}
@ -732,7 +733,7 @@ func _1662365868_add_key_uid_accountsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662365868_add_key_uid_accounts.up.sql", size: 68, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1662365868_add_key_uid_accounts.up.sql", size: 68, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xc6, 0xd8, 0x2f, 0x2f, 0x3b, 0xa8, 0xbd, 0x6d, 0xf6, 0x87, 0x7e, 0xd2, 0xf1, 0xa2, 0xf7, 0x81, 0x6a, 0x23, 0x10, 0xbc, 0xbf, 0x5b, 0xe7, 0x2b, 0x9c, 0xa9, 0x8a, 0x18, 0xbb, 0xd0, 0x86, 0x91}}
return a, nil
}
@ -752,7 +753,7 @@ func _1662447680_add_keypairs_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662447680_add_keypairs_table.up.sql", size: 218, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1662447680_add_keypairs_table.up.sql", size: 218, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xdc, 0x25, 0xa9, 0xc7, 0x63, 0x27, 0x97, 0x35, 0x5f, 0x6b, 0xab, 0x26, 0xcb, 0xf9, 0xbd, 0x5e, 0xac, 0x3, 0xa0, 0x5e, 0xb9, 0x71, 0xa3, 0x1f, 0xb3, 0x4f, 0x7f, 0x79, 0x28, 0x48, 0xbe, 0xc}}
return a, nil
}
@ -772,7 +773,7 @@ func _1662460056_move_favourites_to_saved_addressesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662460056_move_favourites_to_saved_addresses.up.sql", size: 233, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1662460056_move_favourites_to_saved_addresses.up.sql", size: 233, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x10, 0xa2, 0x8c, 0xa3, 0xec, 0xad, 0xdf, 0xc3, 0x48, 0x5, 0x9b, 0x50, 0x25, 0x59, 0xae, 0x7d, 0xee, 0x58, 0xd2, 0x41, 0x27, 0xf2, 0x22, 0x2e, 0x9a, 0xb9, 0x4a, 0xcc, 0x38, 0x6e, 0x3a, 0xb2}}
return a, nil
}
@ -792,7 +793,7 @@ func _1662738097_add_base_fee_transactionUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662738097_add_base_fee_transaction.up.sql", size: 112, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1662738097_add_base_fee_transaction.up.sql", size: 112, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6b, 0xfb, 0x10, 0xae, 0xfc, 0x77, 0x70, 0x98, 0x6f, 0xec, 0xaa, 0xcd, 0x7, 0xc7, 0x74, 0x23, 0xc, 0xd5, 0x1e, 0x82, 0xdd, 0xfe, 0xff, 0x3b, 0xd2, 0x49, 0x10, 0x5b, 0x30, 0xc, 0x2d, 0xb0}}
return a, nil
}
@ -812,7 +813,7 @@ func _1662972194_add_keypairs_tableUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1662972194_add_keypairs_table.up.sql", size: 345, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1662972194_add_keypairs_table.up.sql", size: 345, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xab, 0x76, 0xf2, 0x86, 0xe1, 0x7e, 0xe9, 0x47, 0x32, 0x48, 0xd5, 0x6b, 0xe5, 0xd, 0xab, 0xb7, 0xf1, 0xd4, 0xf1, 0xad, 0x38, 0xa6, 0x11, 0xe7, 0xce, 0x5c, 0x11, 0x11, 0xf, 0x47, 0xb2, 0x4}}
return a, nil
}
@ -832,7 +833,7 @@ func _1664392661_add_third_party_id_to_waku_messagesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1664392661_add_third_party_id_to_waku_messages.up.sql", size: 70, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1664392661_add_third_party_id_to_waku_messages.up.sql", size: 70, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xfd, 0x67, 0x66, 0x9e, 0x66, 0x74, 0xce, 0x1c, 0xb, 0x1b, 0x9d, 0xd5, 0xfc, 0x65, 0xe, 0x83, 0x90, 0x4c, 0x61, 0x4e, 0x6b, 0xe7, 0x86, 0xbe, 0x36, 0x4f, 0x91, 0x36, 0x4, 0x47, 0x7b, 0x82}}
return a, nil
}
@ -852,7 +853,7 @@ func _1664783660_add_sync_info_to_saved_addressesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1664783660_add_sync_info_to_saved_addresses.up.sql", size: 388, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1664783660_add_sync_info_to_saved_addresses.up.sql", size: 388, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x67, 0x7c, 0x3a, 0x95, 0x4e, 0x55, 0xb2, 0xbd, 0xb4, 0x18, 0x93, 0xc1, 0xcf, 0x9f, 0x12, 0xbb, 0x49, 0x8a, 0x2a, 0x6a, 0x2a, 0x7f, 0xad, 0x44, 0xc3, 0xf, 0x3a, 0x79, 0x18, 0xb9, 0x4c, 0x64}}
return a, nil
}
@ -872,7 +873,7 @@ func _1668109917_wakunodesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1668109917_wakunodes.up.sql", size: 99, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1668109917_wakunodes.up.sql", size: 99, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x29, 0xaa, 0x9e, 0x2, 0x66, 0x85, 0x69, 0xa8, 0xd9, 0xe2, 0x4b, 0x8d, 0x2a, 0x9c, 0xdf, 0xd2, 0xef, 0x64, 0x58, 0xe3, 0xa6, 0xe7, 0xc1, 0xd1, 0xc8, 0x9c, 0xc0, 0x2c, 0x1, 0xa8, 0x7b, 0x81}}
return a, nil
}
@ -892,7 +893,7 @@ func _1670249678_display_name_to_settings_sync_clock_tableUpSql() (*asset, error
return nil, err
}
info := bindataFileInfo{name: "1670249678_display_name_to_settings_sync_clock_table.up.sql", size: 83, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1670249678_display_name_to_settings_sync_clock_table.up.sql", size: 83, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x39, 0x18, 0xdc, 0xc4, 0x1f, 0x79, 0x22, 0x16, 0x4d, 0xdf, 0x6c, 0x66, 0xd5, 0xa4, 0x88, 0x5d, 0x5, 0x37, 0xa7, 0x41, 0x5, 0x50, 0xae, 0x12, 0xfa, 0x7e, 0x89, 0x24, 0x5c, 0xae, 0x30, 0xfc}}
return a, nil
}
@ -912,7 +913,7 @@ func _1670836810_add_imported_flag_to_community_archive_hashesUpSql() (*asset, e
return nil, err
}
info := bindataFileInfo{name: "1670836810_add_imported_flag_to_community_archive_hashes.up.sql", size: 144, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1670836810_add_imported_flag_to_community_archive_hashes.up.sql", size: 144, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6f, 0xf, 0xf0, 0xbd, 0xfe, 0x63, 0x25, 0x8f, 0x5e, 0x46, 0x4b, 0x45, 0x31, 0x8b, 0x3e, 0xd8, 0x6b, 0x5d, 0x9d, 0x6d, 0x10, 0x9a, 0x87, 0x4b, 0x18, 0xc6, 0x39, 0x81, 0x6e, 0xe4, 0x75, 0xfb}}
return a, nil
}
@ -932,7 +933,7 @@ func _1671438731_add_magnetlink_uri_to_communities_archive_infoUpSql() (*asset,
return nil, err
}
info := bindataFileInfo{name: "1671438731_add_magnetlink_uri_to_communities_archive_info.up.sql", size: 86, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1671438731_add_magnetlink_uri_to_communities_archive_info.up.sql", size: 86, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xda, 0x8b, 0x4b, 0xd6, 0xd8, 0xe2, 0x3d, 0xf7, 0x6b, 0xcd, 0x1e, 0x70, 0x9, 0x2e, 0x35, 0x4, 0x61, 0xc3, 0xb5, 0x9d, 0xc5, 0x27, 0x21, 0xa, 0x5a, 0xd6, 0x3e, 0xa6, 0x24, 0xa2, 0x12, 0xdf}}
return a, nil
}
@ -952,7 +953,7 @@ func _1672933930_switcher_cardUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1672933930_switcher_card.up.sql", size: 162, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1672933930_switcher_card.up.sql", size: 162, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x39, 0xba, 0xdc, 0xbb, 0x40, 0x4, 0xf2, 0x10, 0xdf, 0xb4, 0xd2, 0x80, 0x8a, 0x74, 0x4d, 0xf6, 0xbc, 0x50, 0x7, 0xd, 0x22, 0x7f, 0xc4, 0xaf, 0xaa, 0xde, 0xdc, 0x71, 0xe9, 0x42, 0x98, 0x36}}
return a, nil
}
@ -972,7 +973,7 @@ func _1674056187_add_price_cacheUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1674056187_add_price_cache.up.sql", size: 255, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1674056187_add_price_cache.up.sql", size: 255, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb7, 0x79, 0x6a, 0x9b, 0x28, 0xd1, 0x22, 0xf0, 0x84, 0x76, 0x40, 0x39, 0x49, 0x15, 0x5d, 0xaa, 0xfd, 0x11, 0xff, 0x13, 0x27, 0x42, 0x12, 0xfa, 0x82, 0xe6, 0x7a, 0xf0, 0x5e, 0x1f, 0xe3, 0xba}}
return a, nil
}
@ -992,7 +993,7 @@ func _1674136690_ens_usernamesUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1674136690_ens_usernames.up.sql", size: 98, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1674136690_ens_usernames.up.sql", size: 98, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x81, 0x7a, 0xf3, 0xa8, 0x88, 0x99, 0xd6, 0x9c, 0x69, 0x48, 0x3c, 0x10, 0xda, 0x72, 0xdc, 0x14, 0xd, 0x6e, 0x8c, 0x82, 0x92, 0x2d, 0x2c, 0xee, 0x4c, 0x70, 0xa4, 0xdc, 0x5c, 0x5, 0x2, 0xc3}}
return a, nil
}
@ -1012,7 +1013,7 @@ func _1674232431_add_balance_historyUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1674232431_add_balance_history.up.sql", size: 698, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "1674232431_add_balance_history.up.sql", size: 698, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf7, 0xb5, 0x18, 0xca, 0x4a, 0x93, 0xbb, 0x6f, 0xa4, 0xee, 0xe4, 0x3e, 0xff, 0x6a, 0x4b, 0xe2, 0xe1, 0x61, 0x28, 0xee, 0xc5, 0x26, 0x57, 0x61, 0x5e, 0x6d, 0x44, 0x1e, 0x85, 0x43, 0x70, 0xa2}}
return a, nil
}
@ -1032,7 +1033,7 @@ func _1676368933_keypairs_to_keycardsUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1676368933_keypairs_to_keycards.up.sql", size: 639, mode: os.FileMode(0644), modTime: time.Unix(1676888614, 0)}
info := bindataFileInfo{name: "1676368933_keypairs_to_keycards.up.sql", size: 639, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x81, 0x93, 0x27, 0x2, 0xf0, 0x37, 0x81, 0x65, 0xa4, 0xb3, 0x5b, 0x60, 0x36, 0x95, 0xfc, 0x81, 0xf0, 0x3b, 0x7c, 0xc3, 0x2c, 0x85, 0xbd, 0x38, 0x46, 0xa4, 0x95, 0x4a, 0x6, 0x3e, 0x74, 0xd5}}
return a, nil
}
@ -1052,7 +1053,7 @@ func _1676951398_add_currency_format_cacheUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1676951398_add_currency_format_cache.up.sql", size: 291, mode: os.FileMode(0644), modTime: time.Unix(1676888614, 0)}
info := bindataFileInfo{name: "1676951398_add_currency_format_cache.up.sql", size: 291, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xf9, 0xa3, 0x76, 0x35, 0xca, 0xf, 0xe8, 0xdf, 0xd9, 0x61, 0xf9, 0xed, 0xfc, 0x6d, 0xf5, 0xe, 0x11, 0x88, 0xbd, 0x14, 0x92, 0xc6, 0x57, 0x53, 0xe, 0xcd, 0x52, 0xf4, 0xa9, 0xb1, 0xdd, 0xfd}}
return a, nil
}
@ -1072,11 +1073,31 @@ func _1676968196_keycards_add_clock_columnUpSql() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "1676968196_keycards_add_clock_column.up.sql", size: 73, mode: os.FileMode(0644), modTime: time.Unix(1677485977, 0)}
info := bindataFileInfo{name: "1676968196_keycards_add_clock_column.up.sql", size: 73, mode: os.FileMode(0664), modTime: time.Unix(1677517539, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4c, 0xf, 0x1c, 0x28, 0x41, 0x57, 0x57, 0x6c, 0xe, 0x75, 0x6b, 0x75, 0x12, 0x0, 0x18, 0x1e, 0x88, 0x1e, 0x45, 0xe0, 0x32, 0xb9, 0xd4, 0xd9, 0x2e, 0xc8, 0xb, 0x80, 0x6, 0x51, 0x3d, 0x28}}
return a, nil
}
var __1676968197_add_fallback_rpc_to_networksUpSql = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x72\xf4\x09\x71\x0d\x52\x08\x71\x74\xf2\x71\x55\xc8\x4b\x2d\x29\xcf\x2f\xca\x2e\x56\x70\x74\x71\x51\x70\xf6\xf7\x09\xf5\xf5\x53\x48\x4b\xcc\xc9\x49\x4a\x4c\xce\x8e\x2f\x2d\xca\x51\x08\x73\x0c\x72\xf6\x70\x0c\x52\xf0\xf3\x0f\x51\xf0\x0b\xf5\xf1\x51\x70\x71\x75\x73\x0c\xf5\x09\x51\x50\x52\xb2\xe6\x0a\x0d\x70\x71\x0c\x41\x32\x24\xd8\x35\x04\x55\xb7\x2d\x48\x19\x20\x00\x00\xff\xff\xe1\xb6\xd7\x0b\x70\x00\x00\x00")
func _1676968197_add_fallback_rpc_to_networksUpSqlBytes() ([]byte, error) {
return bindataRead(
__1676968197_add_fallback_rpc_to_networksUpSql,
"1676968197_add_fallback_rpc_to_networks.up.sql",
)
}
func _1676968197_add_fallback_rpc_to_networksUpSql() (*asset, error) {
bytes, err := _1676968197_add_fallback_rpc_to_networksUpSqlBytes()
if err != nil {
return nil, err
}
info := bindataFileInfo{name: "1676968197_add_fallback_rpc_to_networks.up.sql", size: 112, mode: os.FileMode(0664), modTime: time.Unix(1677517540, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x77, 0x6a, 0xc6, 0x45, 0xfa, 0x62, 0x84, 0x74, 0x6d, 0x7c, 0xd7, 0x1d, 0x79, 0xb6, 0x38, 0x43, 0xa8, 0x8, 0x6b, 0x75, 0x3d, 0x9, 0x2, 0xc5, 0x9f, 0xbb, 0x45, 0x56, 0x4c, 0x4e, 0x17, 0x89}}
return a, nil
}
var _docGo = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x2c\xc9\xb1\x0d\xc4\x20\x0c\x05\xd0\x9e\x29\xfe\x02\xd8\xfd\x6d\xe3\x4b\xac\x2f\x44\x82\x09\x78\x7f\xa5\x49\xfd\xa6\x1d\xdd\xe8\xd8\xcf\x55\x8a\x2a\xe3\x47\x1f\xbe\x2c\x1d\x8c\xfa\x6f\xe3\xb4\x34\xd4\xd9\x89\xbb\x71\x59\xb6\x18\x1b\x35\x20\xa2\x9f\x0a\x03\xa2\xe5\x0d\x00\x00\xff\xff\x60\xcd\x06\xbe\x4a\x00\x00\x00")
func docGoBytes() ([]byte, error) {
@ -1092,7 +1113,7 @@ func docGo() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0644), modTime: time.Unix(1676634063, 0)}
info := bindataFileInfo{name: "doc.go", size: 74, mode: os.FileMode(0664), modTime: time.Unix(1677517511, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xde, 0x7c, 0x28, 0xcd, 0x47, 0xf2, 0xfa, 0x7c, 0x51, 0x2d, 0xd8, 0x38, 0xb, 0xb0, 0x34, 0x9d, 0x4c, 0x62, 0xa, 0x9e, 0x28, 0xc3, 0x31, 0x23, 0xd9, 0xbb, 0x89, 0x9f, 0xa0, 0x89, 0x1f, 0xe8}}
return a, nil
}
@ -1284,6 +1305,8 @@ var _bindata = map[string]func() (*asset, error){
"1676968196_keycards_add_clock_column.up.sql": _1676968196_keycards_add_clock_columnUpSql,
"1676968197_add_fallback_rpc_to_networks.up.sql": _1676968197_add_fallback_rpc_to_networksUpSql,
"doc.go": docGo,
}
@ -1376,6 +1399,7 @@ var _bintree = &bintree{nil, map[string]*bintree{
"1676368933_keypairs_to_keycards.up.sql": &bintree{_1676368933_keypairs_to_keycardsUpSql, map[string]*bintree{}},
"1676951398_add_currency_format_cache.up.sql": &bintree{_1676951398_add_currency_format_cacheUpSql, map[string]*bintree{}},
"1676968196_keycards_add_clock_column.up.sql": &bintree{_1676968196_keycards_add_clock_columnUpSql, map[string]*bintree{}},
"1676968197_add_fallback_rpc_to_networks.up.sql": &bintree{_1676968197_add_fallback_rpc_to_networksUpSql, map[string]*bintree{}},
"doc.go": &bintree{docGo, map[string]*bintree{}},
}}

View File

@ -0,0 +1,2 @@
ALTER TABLE networks ADD COLUMN fallback_url VARCHAR NOT NULL DEFAULT "";
UPDATE networks SET fallback_url = "";

1
go.mod
View File

@ -73,6 +73,7 @@ require (
require github.com/fogleman/gg v1.3.0
require (
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
github.com/gorilla/sessions v1.2.1
github.com/ipfs/go-log/v2 v2.5.1
github.com/ladydascalie/currency v1.6.0

5
go.sum
View File

@ -163,6 +163,7 @@ github.com/VictoriaMetrics/fastcache v1.6.0 h1:C/3Oi3EiBCqufydp1neRZkqcwmEiuRT9c
github.com/VictoriaMetrics/fastcache v1.6.0/go.mod h1:0qHz5QP0GMX4pfmMA/zt5RgfNuXJrTP0zS7DqpHGGTw=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
github.com/alangpierce/go-forceexport v0.0.0-20160317203124-8f1d6941cd75/go.mod h1:uAXEEpARkRhCZfEvy/y0Jcc888f9tHCc1W7/UeEtreE=
@ -1044,6 +1045,7 @@ github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97Dwqy
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190309154008-847fc94819f9/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99 h1:twflg0XRTjwKpxb/jFExr4HGq6on2dEOmnL6FV+fgPw=
github.com/gopherjs/gopherjs v0.0.0-20190910122728-9d188e94fb99/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
@ -1255,6 +1257,7 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
github.com/jsternberg/zap-logfmt v1.0.0/go.mod h1:uvPs/4X51zdkcm5jXl5SYoN+4RK21K8mysFmDaM/h+o=
github.com/jtolds/gls v4.2.1+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
@ -1952,10 +1955,12 @@ github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac h1:wbW+Bybf9pXxnCFAOWZTqkRjAc7rAIwo2e1ArUhiHxg=
github.com/smartystreets/assertions v0.0.0-20190215210624-980c5ac6f3ac/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
github.com/smartystreets/goconvey v0.0.0-20181108003508-044398e4856c/go.mod h1:XDJAKZRPZ1CvBcN2aX5YOUTYGHki24fSF0Iv48Ibg0s=
github.com/smartystreets/goconvey v0.0.0-20190306220146-200a235640ff/go.mod h1:KSQcGKpxUMHk3nbYzs/tIBAM2iDooCn0BmttHOJEbLs=
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/snowflakedb/gosnowflake v1.6.3/go.mod h1:6hLajn6yxuJ4xUHZegMekpq9rnQbGJ7TMwXjgTmA6lg=
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=

View File

@ -499,6 +499,7 @@ type Network struct {
ChainID uint64 `json:"chainId"`
ChainName string `json:"chainName"`
RPCURL string `json:"rpcUrl"`
FallbackURL string `json:"fallbackURL"`
BlockExplorerURL string `json:"blockExplorerUrl,omitempty"`
IconURL string `json:"iconUrl,omitempty"`
NativeCurrencyName string `json:"nativeCurrencyName,omitempty"`

699
rpc/chain/client.go Normal file
View File

@ -0,0 +1,699 @@
package chain
import (
"context"
"fmt"
"math/big"
"time"
"github.com/afex/hystrix-go/hystrix"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/services/rpcstats"
)
type FeeHistory struct {
BaseFeePerGas []string `json:"baseFeePerGas"`
}
type ClientWithFallback struct {
ChainID uint64
main *ethclient.Client
fallback *ethclient.Client
mainRPC *rpc.Client
fallbackRPC *rpc.Client
IsConnected bool
LastCheckedAt int64
}
func NewSimpleClient(main *rpc.Client, chainID uint64) *ClientWithFallback {
hystrix.ConfigureCommand(fmt.Sprintf("ethClient_%d", chainID), hystrix.CommandConfig{
Timeout: 10000,
MaxConcurrentRequests: 100,
SleepWindow: 300000,
ErrorPercentThreshold: 25,
})
return &ClientWithFallback{
ChainID: chainID,
main: ethclient.NewClient(main),
fallback: nil,
mainRPC: main,
fallbackRPC: nil,
IsConnected: true,
LastCheckedAt: time.Now().Unix(),
}
}
func NewClient(main, fallback *rpc.Client, chainID uint64) *ClientWithFallback {
hystrix.ConfigureCommand(fmt.Sprintf("ethClient_%d", chainID), hystrix.CommandConfig{
Timeout: 10000,
MaxConcurrentRequests: 100,
SleepWindow: 300000,
ErrorPercentThreshold: 25,
})
var fallbackEthClient *ethclient.Client
if fallback != nil {
fallbackEthClient = ethclient.NewClient(fallback)
}
return &ClientWithFallback{
ChainID: chainID,
main: ethclient.NewClient(main),
fallback: fallbackEthClient,
mainRPC: main,
fallbackRPC: fallback,
IsConnected: true,
LastCheckedAt: time.Now().Unix(),
}
}
func (c *ClientWithFallback) Close() {
c.main.Close()
if c.fallback != nil {
c.fallback.Close()
}
}
func (c *ClientWithFallback) makeCallNoReturn(main func() error, fallback func() error) error {
output := make(chan struct{}, 1)
c.LastCheckedAt = time.Now().Unix()
errChan := hystrix.Go(fmt.Sprintf("ethClient_%d", c.ChainID), func() error {
err := main()
if err != nil {
return err
}
c.IsConnected = true
output <- struct{}{}
return nil
}, func(err error) error {
if c.fallback == nil {
return err
}
err = fallback()
if err != nil {
c.IsConnected = false
return err
}
c.IsConnected = true
output <- struct{}{}
return nil
})
select {
case <-output:
return nil
case err := <-errChan:
return err
}
}
func (c *ClientWithFallback) makeCallSingleReturn(main func() (any, error), fallback func() (any, error)) (any, error) {
resultChan := make(chan any, 1)
c.LastCheckedAt = time.Now().Unix()
errChan := hystrix.Go(fmt.Sprintf("ethClient_%d", c.ChainID), func() error {
res, err := main()
if err != nil {
return err
}
c.IsConnected = true
resultChan <- res
return nil
}, func(err error) error {
if c.fallback == nil {
return err
}
res, err := fallback()
if err != nil {
c.IsConnected = false
return err
}
c.IsConnected = true
resultChan <- res
return nil
})
select {
case result := <-resultChan:
return result, nil
case err := <-errChan:
return nil, err
}
}
func (c *ClientWithFallback) makeCallDoubleReturn(main func() (any, any, error), fallback func() (any, any, error)) (any, any, error) {
resultChan := make(chan []any, 1)
c.LastCheckedAt = time.Now().Unix()
errChan := hystrix.Go(fmt.Sprintf("ethClient_%d", c.ChainID), func() error {
a, b, err := main()
if err != nil {
return err
}
c.IsConnected = true
resultChan <- []any{a, b}
return nil
}, func(err error) error {
if c.fallback == nil {
return err
}
a, b, err := fallback()
if err != nil {
c.IsConnected = false
return err
}
c.IsConnected = true
resultChan <- []any{a, b}
return nil
})
select {
case result := <-resultChan:
return result[0], result[1], nil
case err := <-errChan:
return nil, nil, err
}
}
func (c *ClientWithFallback) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
rpcstats.CountCall("eth_BlockByHash")
block, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.BlockByHash(ctx, hash) },
func() (any, error) { return c.fallback.BlockByHash(ctx, hash) },
)
if err != nil {
return nil, err
}
return block.(*types.Block), nil
}
func (c *ClientWithFallback) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
rpcstats.CountCall("eth_BlockByNumber")
block, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.BlockByNumber(ctx, number) },
func() (any, error) { return c.fallback.BlockByNumber(ctx, number) },
)
if err != nil {
return nil, err
}
return block.(*types.Block), nil
}
func (c *ClientWithFallback) BlockNumber(ctx context.Context) (uint64, error) {
rpcstats.CountCall("eth_BlockNumber")
number, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.BlockNumber(ctx) },
func() (any, error) { return c.fallback.BlockNumber(ctx) },
)
if err != nil {
return 0, err
}
return number.(uint64), nil
}
func (c *ClientWithFallback) PeerCount(ctx context.Context) (uint64, error) {
rpcstats.CountCall("eth_PeerCount")
peerCount, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.PeerCount(ctx) },
func() (any, error) { return c.fallback.PeerCount(ctx) },
)
if err != nil {
return 0, err
}
return peerCount.(uint64), nil
}
func (c *ClientWithFallback) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
rpcstats.CountCall("eth_HeaderByHash")
header, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.HeaderByHash(ctx, hash) },
func() (any, error) { return c.fallback.HeaderByHash(ctx, hash) },
)
if err != nil {
return nil, err
}
return header.(*types.Header), nil
}
func (c *ClientWithFallback) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
rpcstats.CountCall("eth_HeaderByNumber")
header, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.HeaderByNumber(ctx, number) },
func() (any, error) { return c.fallback.HeaderByNumber(ctx, number) },
)
if err != nil {
return nil, err
}
return header.(*types.Header), nil
}
func (c *ClientWithFallback) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, bool, error) {
rpcstats.CountCall("eth_TransactionByHash")
tx, isPending, err := c.makeCallDoubleReturn(
func() (any, any, error) { return c.main.TransactionByHash(ctx, hash) },
func() (any, any, error) { return c.fallback.TransactionByHash(ctx, hash) },
)
if err != nil {
return nil, false, err
}
return tx.(*types.Transaction), isPending.(bool), nil
}
func (c *ClientWithFallback) TransactionSender(ctx context.Context, tx *types.Transaction, block common.Hash, index uint) (common.Address, error) {
rpcstats.CountCall("eth_TransactionSender")
address, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.TransactionSender(ctx, tx, block, index) },
func() (any, error) { return c.fallback.TransactionSender(ctx, tx, block, index) },
)
return address.(common.Address), err
}
func (c *ClientWithFallback) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
rpcstats.CountCall("eth_TransactionCount")
count, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.TransactionCount(ctx, blockHash) },
func() (any, error) { return c.fallback.TransactionCount(ctx, blockHash) },
)
if err != nil {
return 0, err
}
return count.(uint), nil
}
func (c *ClientWithFallback) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) {
rpcstats.CountCall("eth_TransactionInBlock")
transactions, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.TransactionInBlock(ctx, blockHash, index) },
func() (any, error) { return c.fallback.TransactionInBlock(ctx, blockHash, index) },
)
if err != nil {
return nil, err
}
return transactions.(*types.Transaction), nil
}
func (c *ClientWithFallback) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
rpcstats.CountCall("eth_TransactionReceipt")
receipt, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.TransactionReceipt(ctx, txHash) },
func() (any, error) { return c.fallback.TransactionReceipt(ctx, txHash) },
)
if err != nil {
return nil, err
}
return receipt.(*types.Receipt), nil
}
func (c *ClientWithFallback) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) {
rpcstats.CountCall("eth_SyncProgress")
progress, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.SyncProgress(ctx) },
func() (any, error) { return c.fallback.SyncProgress(ctx) },
)
if err != nil {
return nil, err
}
return progress.(*ethereum.SyncProgress), nil
}
func (c *ClientWithFallback) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
rpcstats.CountCall("eth_SubscribeNewHead")
sub, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.SubscribeNewHead(ctx, ch) },
func() (any, error) { return c.fallback.SubscribeNewHead(ctx, ch) },
)
if err != nil {
return nil, err
}
return sub.(ethereum.Subscription), nil
}
func (c *ClientWithFallback) NetworkID(ctx context.Context) (*big.Int, error) {
rpcstats.CountCall("eth_NetworkID")
networkID, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.NetworkID(ctx) },
func() (any, error) { return c.fallback.NetworkID(ctx) },
)
if err != nil {
return nil, err
}
return networkID.(*big.Int), nil
}
func (c *ClientWithFallback) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
rpcstats.CountCall("eth_BalanceAt")
balance, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.BalanceAt(ctx, account, blockNumber) },
func() (any, error) { return c.fallback.BalanceAt(ctx, account, blockNumber) },
)
if err != nil {
return nil, err
}
return balance.(*big.Int), nil
}
func (c *ClientWithFallback) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
rpcstats.CountCall("eth_StorageAt")
storage, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.StorageAt(ctx, account, key, blockNumber) },
func() (any, error) { return c.fallback.StorageAt(ctx, account, key, blockNumber) },
)
if err != nil {
return nil, err
}
return storage.([]byte), nil
}
func (c *ClientWithFallback) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
rpcstats.CountCall("eth_CodeAt")
code, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.CodeAt(ctx, account, blockNumber) },
func() (any, error) { return c.fallback.CodeAt(ctx, account, blockNumber) },
)
if err != nil {
return nil, err
}
return code.([]byte), nil
}
func (c *ClientWithFallback) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
rpcstats.CountCall("eth_NonceAt")
nonce, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.NonceAt(ctx, account, blockNumber) },
func() (any, error) { return c.fallback.NonceAt(ctx, account, blockNumber) },
)
if err != nil {
return 0, err
}
return nonce.(uint64), nil
}
func (c *ClientWithFallback) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
rpcstats.CountCall("eth_FilterLogs")
logs, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.FilterLogs(ctx, q) },
func() (any, error) { return c.fallback.FilterLogs(ctx, q) },
)
if err != nil {
return nil, err
}
return logs.([]types.Log), nil
}
func (c *ClientWithFallback) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- types.Log) (ethereum.Subscription, error) {
rpcstats.CountCall("eth_SubscribeFilterLogs")
sub, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.SubscribeFilterLogs(ctx, q, ch) },
func() (any, error) { return c.fallback.SubscribeFilterLogs(ctx, q, ch) },
)
if err != nil {
return nil, err
}
return sub.(ethereum.Subscription), nil
}
func (c *ClientWithFallback) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) {
rpcstats.CountCall("eth_PendingBalanceAt")
balance, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.PendingBalanceAt(ctx, account) },
func() (any, error) { return c.fallback.PendingBalanceAt(ctx, account) },
)
if err != nil {
return nil, err
}
return balance.(*big.Int), nil
}
func (c *ClientWithFallback) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) {
rpcstats.CountCall("eth_PendingStorageAt")
storage, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.PendingStorageAt(ctx, account, key) },
func() (any, error) { return c.fallback.PendingStorageAt(ctx, account, key) },
)
if err != nil {
return nil, err
}
return storage.([]byte), nil
}
func (c *ClientWithFallback) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
rpcstats.CountCall("eth_PendingCodeAt")
code, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.PendingCodeAt(ctx, account) },
func() (any, error) { return c.fallback.PendingCodeAt(ctx, account) },
)
if err != nil {
return nil, err
}
return code.([]byte), nil
}
func (c *ClientWithFallback) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
rpcstats.CountCall("eth_PendingNonceAt")
nonce, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.PendingNonceAt(ctx, account) },
func() (any, error) { return c.fallback.PendingNonceAt(ctx, account) },
)
if err != nil {
return 0, err
}
return nonce.(uint64), nil
}
func (c *ClientWithFallback) PendingTransactionCount(ctx context.Context) (uint, error) {
rpcstats.CountCall("eth_PendingTransactionCount")
count, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.PendingTransactionCount(ctx) },
func() (any, error) { return c.fallback.PendingTransactionCount(ctx) },
)
if err != nil {
return 0, err
}
return count.(uint), nil
}
func (c *ClientWithFallback) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
rpcstats.CountCall("eth_CallContract")
data, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.CallContract(ctx, msg, blockNumber) },
func() (any, error) { return c.fallback.CallContract(ctx, msg, blockNumber) },
)
if err != nil {
return nil, err
}
return data.([]byte), nil
}
func (c *ClientWithFallback) CallContractAtHash(ctx context.Context, msg ethereum.CallMsg, blockHash common.Hash) ([]byte, error) {
rpcstats.CountCall("eth_CallContractAtHash")
data, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.CallContractAtHash(ctx, msg, blockHash) },
func() (any, error) { return c.fallback.CallContractAtHash(ctx, msg, blockHash) },
)
if err != nil {
return nil, err
}
return data.([]byte), nil
}
func (c *ClientWithFallback) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
rpcstats.CountCall("eth_PendingCallContract")
data, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.PendingCallContract(ctx, msg) },
func() (any, error) { return c.fallback.PendingCallContract(ctx, msg) },
)
if err != nil {
return nil, err
}
return data.([]byte), nil
}
func (c *ClientWithFallback) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
rpcstats.CountCall("eth_SuggestGasPrice")
gasPrice, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.SuggestGasPrice(ctx) },
func() (any, error) { return c.fallback.SuggestGasPrice(ctx) },
)
if err != nil {
return nil, err
}
return gasPrice.(*big.Int), nil
}
func (c *ClientWithFallback) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
rpcstats.CountCall("eth_SuggestGasTipCap")
tip, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.SuggestGasTipCap(ctx) },
func() (any, error) { return c.fallback.SuggestGasTipCap(ctx) },
)
if err != nil {
return nil, err
}
return tip.(*big.Int), nil
}
func (c *ClientWithFallback) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) {
rpcstats.CountCall("eth_FeeHistory")
feeHistory, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) },
func() (any, error) { return c.fallback.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) },
)
if err != nil {
return nil, err
}
return feeHistory.(*ethereum.FeeHistory), nil
}
func (c *ClientWithFallback) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) {
rpcstats.CountCall("eth_EstimateGas")
estimate, err := c.makeCallSingleReturn(
func() (any, error) { return c.main.EstimateGas(ctx, msg) },
func() (any, error) { return c.fallback.EstimateGas(ctx, msg) },
)
if err != nil {
return 0, err
}
return estimate.(uint64), nil
}
func (c *ClientWithFallback) SendTransaction(ctx context.Context, tx *types.Transaction) error {
rpcstats.CountCall("eth_SendTransaction")
return c.makeCallNoReturn(
func() error { return c.main.SendTransaction(ctx, tx) },
func() error { return c.fallback.SendTransaction(ctx, tx) },
)
}
func (c *ClientWithFallback) CallContext(ctx context.Context, result interface{}, method string, args ...interface{}) error {
rpcstats.CountCall("eth_CallContext")
return c.makeCallNoReturn(
func() error { return c.mainRPC.CallContext(ctx, result, method, args...) },
func() error { return c.fallbackRPC.CallContext(ctx, result, method, args...) },
)
}
func (c *ClientWithFallback) ToBigInt() *big.Int {
return big.NewInt(int64(c.ChainID))
}
func (c *ClientWithFallback) GetBaseFeeFromBlock(blockNumber *big.Int) (string, error) {
rpcstats.CountCall("eth_GetBaseFeeFromBlock")
var feeHistory FeeHistory
err := c.mainRPC.Call(&feeHistory, "eth_feeHistory", "0x1", (*hexutil.Big)(blockNumber), nil)
if err != nil {
if err.Error() == "the method eth_feeHistory does not exist/is not available" {
return "", nil
}
return "", err
}
var baseGasFee string = ""
if len(feeHistory.BaseFeePerGas) > 0 {
baseGasFee = feeHistory.BaseFeePerGas[0]
}
return baseGasFee, err
}

View File

@ -10,11 +10,11 @@ import (
"sync"
"time"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
gethrpc "github.com/ethereum/go-ethereum/rpc"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/rpc/network"
"github.com/status-im/status-go/services/rpcstats"
)
@ -43,8 +43,8 @@ type Client struct {
UpstreamChainID uint64
local *gethrpc.Client
upstream *gethrpc.Client
rpcClients map[uint64]*gethrpc.Client
upstream *chain.ClientWithFallback
rpcClients map[uint64]*chain.ClientWithFallback
router *router
NetworkManager *network.Manager
@ -73,7 +73,7 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U
local: client,
NetworkManager: networkManager,
handlers: make(map[string]Handler),
rpcClients: make(map[uint64]*gethrpc.Client),
rpcClients: make(map[uint64]*chain.ClientWithFallback),
log: log,
}
@ -81,10 +81,11 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U
c.UpstreamChainID = upstreamChainID
c.upstreamEnabled = upstream.Enabled
c.upstreamURL = upstream.URL
c.upstream, err = gethrpc.Dial(c.upstreamURL)
upstreamClient, err := gethrpc.Dial(c.upstreamURL)
if err != nil {
return nil, fmt.Errorf("dial upstream server: %s", err)
}
c.upstream = chain.NewSimpleClient(upstreamClient, upstreamChainID)
}
c.router = newRouter(c.upstreamEnabled)
@ -92,21 +93,16 @@ func NewClient(client *gethrpc.Client, upstreamChainID uint64, upstream params.U
return &c, nil
}
func (c *Client) getRPCClientWithCache(chainID uint64) (*gethrpc.Client, error) {
if !c.upstreamEnabled {
return c.local, nil
}
if c.UpstreamChainID == chainID {
return c.upstream, nil
}
func (c *Client) getClientUsingCache(chainID uint64) (*chain.ClientWithFallback, error) {
if rpcClient, ok := c.rpcClients[chainID]; ok {
return rpcClient, nil
}
network := c.NetworkManager.Find(chainID)
if network == nil {
if c.UpstreamChainID == chainID {
return c.upstream, nil
}
return nil, fmt.Errorf("could not find network: %d", chainID)
}
@ -115,18 +111,40 @@ func (c *Client) getRPCClientWithCache(chainID uint64) (*gethrpc.Client, error)
return nil, fmt.Errorf("dial upstream server: %s", err)
}
c.rpcClients[chainID] = rpcClient
return rpcClient, nil
var rpcFallbackClient *gethrpc.Client
if len(network.FallbackURL) > 0 {
rpcFallbackClient, err = gethrpc.Dial(network.FallbackURL)
if err != nil {
return nil, fmt.Errorf("dial upstream server: %s", err)
}
}
client := chain.NewClient(rpcClient, rpcFallbackClient, chainID)
c.rpcClients[chainID] = client
return client, nil
}
// Ethclient returns ethclient.Client per chain
func (c *Client) EthClient(chainID uint64) (*ethclient.Client, error) {
rpcClient, err := c.getRPCClientWithCache(chainID)
func (c *Client) EthClient(chainID uint64) (*chain.ClientWithFallback, error) {
client, err := c.getClientUsingCache(chainID)
if err != nil {
return nil, err
}
return ethclient.NewClient(rpcClient), nil
return client, nil
}
func (c *Client) EthClients(chainIDs []uint64) ([]*chain.ClientWithFallback, error) {
clients := make([]*chain.ClientWithFallback, 0)
for _, chainID := range chainIDs {
client, err := c.getClientUsingCache(chainID)
if err != nil {
return nil, err
}
clients = append(clients, client)
}
return clients, nil
}
// UpdateUpstreamURL changes the upstream RPC client URL, if the upstream is enabled.
@ -139,9 +157,8 @@ func (c *Client) UpdateUpstreamURL(url string) error {
if err != nil {
return err
}
c.Lock()
c.upstream = rpcClient
c.upstream = chain.NewSimpleClient(rpcClient, c.UpstreamChainID)
c.upstreamURL = url
c.Unlock()
@ -195,11 +212,11 @@ func (c *Client) CallContextIgnoringLocalHandlers(ctx context.Context, result in
}
if c.router.routeRemote(method) {
ethClient, err := c.getRPCClientWithCache(chainID)
client, err := c.getClientUsingCache(chainID)
if err != nil {
return err
}
return ethClient.CallContext(ctx, result, method, args...)
return client.CallContext(ctx, result, method, args...)
}
if c.local == nil {

View File

@ -8,7 +8,7 @@ import (
"github.com/status-im/status-go/params"
)
const baseQuery = "SELECT chain_id, chain_name, rpc_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name FROM networks"
const baseQuery = "SELECT chain_id, chain_name, rpc_url, fallback_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name FROM networks"
func newNetworksQuery() *networksQuery {
buf := bytes.NewBuffer(nil)
@ -56,7 +56,7 @@ func (nq *networksQuery) exec(db *sql.DB) ([]*params.Network, error) {
for rows.Next() {
network := params.Network{}
err := rows.Scan(
&network.ChainID, &network.ChainName, &network.RPCURL, &network.BlockExplorerURL, &network.IconURL,
&network.ChainID, &network.ChainName, &network.RPCURL, &network.FallbackURL, &network.BlockExplorerURL, &network.IconURL,
&network.NativeCurrencyName, &network.NativeCurrencySymbol,
&network.NativeCurrencyDecimals, &network.IsTest, &network.Layer, &network.Enabled, &network.ChainColor, &network.ShortName,
)
@ -121,6 +121,14 @@ func (nm *Manager) Init(networks []params.Network) error {
errors += fmt.Sprintf("error updating network rpc_url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
}
}
if currentNetworks[j].FallbackURL != networks[i].FallbackURL {
// Update fallback_url if it's different
err := nm.UpdateFallbackURL(currentNetworks[j].ChainID, networks[i].FallbackURL)
if err != nil {
errors += fmt.Sprintf("error updating network fallback_url for ChainID: %d, %s", currentNetworks[j].ChainID, err.Error())
}
}
break
}
}
@ -143,8 +151,8 @@ func (nm *Manager) Init(networks []params.Network) error {
func (nm *Manager) Upsert(network *params.Network) error {
_, err := nm.db.Exec(
"INSERT OR REPLACE INTO networks (chain_id, chain_name, rpc_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
network.ChainID, network.ChainName, network.RPCURL, network.BlockExplorerURL, network.IconURL,
"INSERT OR REPLACE INTO networks (chain_id, chain_name, rpc_url, fallback_url, block_explorer_url, icon_url, native_currency_name, native_currency_symbol, native_currency_decimals, is_test, layer, enabled, chain_color, short_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
network.ChainID, network.ChainName, network.RPCURL, network.FallbackURL, network.BlockExplorerURL, network.IconURL,
network.NativeCurrencyName, network.NativeCurrencySymbol, network.NativeCurrencyDecimals,
network.IsTest, network.Layer, network.Enabled, network.ChainColor, network.ShortName,
)
@ -161,6 +169,11 @@ func (nm *Manager) UpdateRPCURL(chainID uint64, rpcURL string) error {
return err
}
func (nm *Manager) UpdateFallbackURL(chainID uint64, fallbackURL string) error {
_, err := nm.db.Exec(`UPDATE networks SET fallback_url = ? WHERE chain_id = ?`, fallbackURL, chainID)
return err
}
func (nm *Manager) Find(chainID uint64) *params.Network {
networks, err := newNetworksQuery().filterChainID(chainID).exec(nm.db)
if len(networks) != 1 || err != nil {

View File

@ -12,9 +12,9 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/bridge"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/services/wallet/currency"
"github.com/status-im/status-go/services/wallet/history"
"github.com/status-im/status-go/services/wallet/thirdparty"
@ -114,15 +114,15 @@ func (api *API) GetCachedBalancesbyChainID(ctx context.Context, chainID uint64,
// GetTokensBalances return mapping of token balances for every account.
func (api *API) GetTokensBalances(ctx context.Context, accounts, addresses []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
chainClient, err := chain.NewLegacyClient(api.s.rpcClient)
chainClient, err := api.s.rpcClient.EthClient(api.s.rpcClient.UpstreamChainID)
if err != nil {
return nil, err
}
return api.s.tokenManager.GetBalances(ctx, []*chain.Client{chainClient}, accounts, addresses)
return api.s.tokenManager.GetBalances(ctx, []*chain.ClientWithFallback{chainClient}, accounts, addresses)
}
func (api *API) GetTokensBalancesForChainIDs(ctx context.Context, chainIDs []uint64, accounts, addresses []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
clients, err := chain.NewClients(api.s.rpcClient, chainIDs)
clients, err := api.s.rpcClient.EthClients(chainIDs)
if err != nil {
return nil, err
}
@ -277,7 +277,7 @@ func (api *API) DeletePendingTransactionByChainID(ctx context.Context, chainID u
}
func (api *API) WatchTransaction(ctx context.Context, transactionHash common.Hash) error {
chainClient, err := chain.NewLegacyClient(api.s.rpcClient)
chainClient, err := api.s.rpcClient.EthClient(api.s.rpcClient.UpstreamChainID)
if err != nil {
return err
}
@ -285,7 +285,7 @@ func (api *API) WatchTransaction(ctx context.Context, transactionHash common.Has
}
func (api *API) WatchTransactionByChainID(ctx context.Context, chainID uint64, transactionHash common.Hash) error {
chainClient, err := chain.NewClient(api.s.rpcClient, chainID)
chainClient, err := api.s.rpcClient.EthClient(chainID)
if err != nil {
return err
}

View File

@ -1,170 +0,0 @@
package chain
import (
"context"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/rpcstats"
)
var ChainClientInstances = make(map[uint64]*Client)
type Client struct {
eth *ethclient.Client
ChainID uint64
rpcClient *rpc.Client
IsConnected bool
LastCheckedAt int64
IsConnectedLock sync.RWMutex
}
type FeeHistory struct {
BaseFeePerGas []string `json:"baseFeePerGas"`
}
func NewClient(rpc *rpc.Client, chainID uint64) (*Client, error) {
if client, ok := ChainClientInstances[chainID]; ok {
return client, nil
}
ethClient, err := rpc.EthClient(chainID)
if err != nil {
return nil, err
}
client := &Client{eth: ethClient, ChainID: chainID, rpcClient: rpc, IsConnected: true, LastCheckedAt: time.Now().Unix()}
ChainClientInstances[chainID] = client
return client, nil
}
func NewLegacyClient(rpc *rpc.Client) (*Client, error) {
return NewClient(rpc, rpc.UpstreamChainID)
}
func NewClients(rpc *rpc.Client, chainIDs []uint64) (res []*Client, err error) {
for _, chainID := range chainIDs {
client, err := NewClient(rpc, chainID)
if err != nil {
return nil, err
}
res = append(res, client)
}
return res, nil
}
func (cc *Client) toggleIsConnected(err error) {
cc.IsConnectedLock.Lock()
defer cc.IsConnectedLock.Unlock()
cc.LastCheckedAt = time.Now().Unix()
if err != nil {
cc.IsConnected = false
} else {
cc.IsConnected = true
}
}
func (cc *Client) ToBigInt() *big.Int {
return big.NewInt(int64(cc.ChainID))
}
func (cc *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
rpcstats.CountCall("eth_getBlockByHash")
resp, err := cc.eth.HeaderByHash(ctx, hash)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
rpcstats.CountCall("eth_getBlockByNumber")
resp, err := cc.eth.HeaderByNumber(ctx, number)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) {
rpcstats.CountCall("eth_getBlockByHash")
resp, err := cc.eth.BlockByHash(ctx, hash)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) {
rpcstats.CountCall("eth_getBlockByNumber")
resp, err := cc.eth.BlockByNumber(ctx, number)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
rpcstats.CountCall("eth_getBalance")
resp, err := cc.eth.BalanceAt(ctx, account, blockNumber)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
rpcstats.CountCall("eth_getTransactionCount")
resp, err := cc.eth.NonceAt(ctx, account, blockNumber)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
rpcstats.CountCall("eth_getTransactionReceipt")
resp, err := cc.eth.TransactionReceipt(ctx, txHash)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
rpcstats.CountCall("eth_getTransactionByHash")
tx, isPending, err = cc.eth.TransactionByHash(ctx, hash)
defer cc.toggleIsConnected(err)
return tx, isPending, err
}
func (cc *Client) FilterLogs(ctx context.Context, q ethereum.FilterQuery) ([]types.Log, error) {
rpcstats.CountCall("eth_getLogs")
resp, err := cc.eth.FilterLogs(ctx, q)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) CodeAt(ctx context.Context, contract common.Address, blockNumber *big.Int) ([]byte, error) {
rpcstats.CountCall("eth_getCode")
resp, err := cc.eth.CodeAt(ctx, contract, blockNumber)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) CallContract(ctx context.Context, call ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
rpcstats.CountCall("eth_call")
resp, err := cc.eth.CallContract(ctx, call, blockNumber)
defer cc.toggleIsConnected(err)
return resp, err
}
func (cc *Client) GetBaseFeeFromBlock(blockNumber *big.Int) (string, error) {
var feeHistory FeeHistory
err := cc.rpcClient.Call(&feeHistory, cc.ChainID, "eth_feeHistory", "0x1", (*hexutil.Big)(blockNumber), nil)
if err != nil {
if err.Error() == "the method eth_feeHistory does not exist/is not available" {
return "", nil
}
return "", err
}
var baseGasFee string = ""
if len(feeHistory.BaseFeePerGas) > 0 {
baseGasFee = feeHistory.BaseFeePerGas[0]
}
return baseGasFee, err
}

View File

@ -21,7 +21,7 @@ import (
statusrpc "github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/rpc/network"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/services/wallet/market"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/services/wallet/walletevent"
@ -147,7 +147,7 @@ func (s *Service) isTokenVisible(tokenSymbol string) bool {
// Native token implementation of DataSource interface
type chainClientSource struct {
chainClient *chain.Client
chainClient *chain.ClientWithFallback
currency string
}
@ -505,7 +505,7 @@ func (s *Service) updateBalanceHistoryForAllEnabledNetworks(ctx context.Context)
}
var dataSource DataSource
chainClient, err := chain.NewClient(s.rpcClient, network.ChainID)
chainClient, err := s.rpcClient.EthClient(network.ChainID)
if err != nil {
return err
}

View File

@ -12,7 +12,6 @@ import (
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/services/wallet/market"
"github.com/status-im/status-go/services/wallet/thirdparty"
@ -211,11 +210,10 @@ func (r *Reader) GetWalletToken(ctx context.Context, addresses []common.Address)
})
group.Add(func(parent context.Context) error {
clients, err := chain.NewClients(r.rpcClient, chainIDs)
clients, err := r.rpcClient.EthClients(chainIDs)
if err != nil {
return err
}
balances, err = r.tokenManager.GetBalancesByChain(ctx, clients, addresses, tokenAddresses)
if err != nil {
return err

View File

@ -23,7 +23,6 @@ import (
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/bigint"
"github.com/status-im/status-go/services/wallet/bridge"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/services/wallet/token"
"github.com/status-im/status-go/transactions"
)
@ -428,12 +427,12 @@ func (r *Router) requireApproval(ctx context.Context, bridge bridge.Bridge, acco
}
func (r *Router) getBalance(ctx context.Context, network *params.Network, token *token.Token, account common.Address) (*big.Int, error) {
clients, err := chain.NewClients(r.s.rpcClient, []uint64{network.ChainID})
client, err := r.s.rpcClient.EthClient(network.ChainID)
if err != nil {
return nil, err
}
return r.s.tokenManager.GetBalance(ctx, clients[0], account, token.Address)
return r.s.tokenManager.GetBalance(ctx, client, account, token.Address)
}
func (r *Router) suggestedRoutes(

View File

@ -3,6 +3,7 @@ package wallet
import (
"context"
"database/sql"
"time"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
@ -15,7 +16,6 @@ import (
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/services/ens"
"github.com/status-im/status-go/services/stickers"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/services/wallet/currency"
"github.com/status-im/status-go/services/wallet/history"
"github.com/status-im/status-go/services/wallet/market"
@ -33,7 +33,7 @@ type Connection struct {
}
type ConnectedResult struct {
Blockchain map[uint64]Connection `json:"blockchain"`
Blockchains map[uint64]Connection `json:"blockchains"`
Market Connection `json:"market"`
Collectibles map[uint64]Connection `json:"collectibles"`
}
@ -166,11 +166,21 @@ func (s *Service) IsStarted() bool {
}
func (s *Service) CheckConnected(ctx context.Context) *ConnectedResult {
blockchain := make(map[uint64]Connection)
for chainID, client := range chain.ChainClientInstances {
blockchain[chainID] = Connection{
Up: client.IsConnected,
LastCheckedAt: client.LastCheckedAt,
networks, err := s.rpcClient.NetworkManager.Get(false)
blockchains := make(map[uint64]Connection)
if err == nil {
for _, network := range networks {
ethClient, err := s.rpcClient.EthClient(network.ChainID)
if err != nil {
blockchains[network.ChainID] = Connection{
Up: true,
LastCheckedAt: time.Now().Unix(),
}
}
blockchains[network.ChainID] = Connection{
Up: ethClient.IsConnected,
LastCheckedAt: ethClient.LastCheckedAt,
}
}
}
@ -182,7 +192,7 @@ func (s *Service) CheckConnected(ctx context.Context) *ConnectedResult {
}
}
return &ConnectedResult{
Blockchain: blockchain,
Blockchains: blockchains,
Collectibles: collectibles,
Market: Connection{
Up: s.marketManager.IsConnected,

View File

@ -16,9 +16,9 @@ import (
"github.com/status-im/status-go/contracts/ierc20"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/rpc/network"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/chain"
)
var requestTimeout = 20 * time.Second
@ -393,7 +393,7 @@ func (tm *Manager) DeleteCustom(chainID uint64, address common.Address) error {
return err
}
func (tm *Manager) GetTokenBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
func (tm *Manager) GetTokenBalance(ctx context.Context, client *chain.ClientWithFallback, account common.Address, token common.Address) (*big.Int, error) {
caller, err := ierc20.NewIERC20Caller(token, client)
if err != nil {
return nil, err
@ -404,7 +404,7 @@ func (tm *Manager) GetTokenBalance(ctx context.Context, client *chain.Client, ac
}, account)
}
func (tm *Manager) GetTokenBalanceAt(ctx context.Context, client *chain.Client, account common.Address, token common.Address, blockNumber *big.Int) (*big.Int, error) {
func (tm *Manager) GetTokenBalanceAt(ctx context.Context, client *chain.ClientWithFallback, account common.Address, token common.Address, blockNumber *big.Int) (*big.Int, error) {
caller, err := ierc20.NewIERC20Caller(token, client)
if err != nil {
return nil, err
@ -416,11 +416,11 @@ func (tm *Manager) GetTokenBalanceAt(ctx context.Context, client *chain.Client,
}, account)
}
func (tm *Manager) GetChainBalance(ctx context.Context, client *chain.Client, account common.Address) (*big.Int, error) {
func (tm *Manager) GetChainBalance(ctx context.Context, client *chain.ClientWithFallback, account common.Address) (*big.Int, error) {
return client.BalanceAt(ctx, account, nil)
}
func (tm *Manager) GetBalance(ctx context.Context, client *chain.Client, account common.Address, token common.Address) (*big.Int, error) {
func (tm *Manager) GetBalance(ctx context.Context, client *chain.ClientWithFallback, account common.Address, token common.Address) (*big.Int, error) {
if token == nativeChainAddress {
return tm.GetChainBalance(ctx, client, account)
}
@ -428,7 +428,7 @@ func (tm *Manager) GetBalance(ctx context.Context, client *chain.Client, account
return tm.GetTokenBalance(ctx, client, account, token)
}
func (tm *Manager) GetBalances(parent context.Context, clients []*chain.Client, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
func (tm *Manager) GetBalances(parent context.Context, clients []*chain.ClientWithFallback, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
var (
group = async.NewAtomicGroup(parent)
mu sync.Mutex
@ -558,7 +558,7 @@ func (tm *Manager) GetBalances(parent context.Context, clients []*chain.Client,
return response, group.Error()
}
func (tm *Manager) GetBalancesByChain(parent context.Context, clients []*chain.Client, accounts, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
func (tm *Manager) GetBalancesByChain(parent context.Context, clients []*chain.ClientWithFallback, accounts, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) {
var (
group = async.NewAtomicGroup(parent)
mu sync.Mutex

View File

@ -16,10 +16,10 @@ import (
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/bigint"
"github.com/status-im/status-go/services/wallet/bridge"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/transactions"
)
@ -218,7 +218,7 @@ func (tm *TransactionManager) deletePending(chainID uint64, hash common.Hash) er
return err
}
func (tm *TransactionManager) watch(ctx context.Context, transactionHash common.Hash, client *chain.Client) error {
func (tm *TransactionManager) watch(ctx context.Context, transactionHash common.Hash, client *chain.ClientWithFallback) error {
watchTxCommand := &watchTransactionCommand{
hash: transactionHash,
client: client,
@ -316,7 +316,7 @@ func (tm *TransactionManager) getVerifiedWalletAccount(address, password string)
}
type watchTransactionCommand struct {
client *chain.Client
client *chain.ClientWithFallback
hash common.Hash
}

View File

@ -9,8 +9,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/services/wallet/bigint"
"github.com/status-im/status-go/services/wallet/chain"
)
type BlocksRange struct {
@ -63,7 +63,7 @@ func (b *Block) mergeBlocksRanges(chainIDs []uint64, accounts []common.Address)
return nil
}
func (b *Block) setInitialBlocksRange(chainClient *chain.Client) error {
func (b *Block) setInitialBlocksRange(chainClient *chain.ClientWithFallback) error {
accountsDB, err := accounts.NewDB(b.db)
if err != nil {
return err

View File

@ -10,8 +10,8 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/services/wallet/walletevent"
)
@ -45,7 +45,7 @@ type ethHistoricalCommand struct {
db *Database
eth Downloader
address common.Address
chainClient *chain.Client
chainClient *chain.ClientWithFallback
balanceCache *balanceCache
feed *event.Feed
foundHeaders []*DBHeader
@ -96,7 +96,7 @@ type erc20HistoricalCommand struct {
db *Database
erc20 BatchDownloader
address common.Address
chainClient *chain.Client
chainClient *chain.ClientWithFallback
feed *event.Feed
iterator *IterativeDownloader
@ -164,7 +164,7 @@ type controlCommand struct {
block *Block
eth *ETHDownloader
erc20 *ERC20TransfersDownloader
chainClient *chain.Client
chainClient *chain.ClientWithFallback
feed *event.Feed
errorsCount int
nonArchivalRPCNode bool
@ -337,7 +337,7 @@ type transfersCommand struct {
eth *ETHDownloader
block *big.Int
address common.Address
chainClient *chain.Client
chainClient *chain.ClientWithFallback
fetchedTransfers []Transfer
}
@ -370,7 +370,7 @@ type loadTransfersCommand struct {
accounts []common.Address
db *Database
block *Block
chainClient *chain.Client
chainClient *chain.ClientWithFallback
blocksByAddress map[common.Address][]*big.Int
foundTransfersByAddress map[common.Address][]Transfer
}
@ -405,7 +405,7 @@ func (c *loadTransfersCommand) Run(parent context.Context) (err error) {
type findAndCheckBlockRangeCommand struct {
accounts []common.Address
db *Database
chainClient *chain.Client
chainClient *chain.ClientWithFallback
balanceCache *balanceCache
feed *event.Feed
fromByAddress map[common.Address]*LastKnownBlock
@ -570,7 +570,7 @@ func (c *findAndCheckBlockRangeCommand) fastIndexErc20(ctx context.Context, from
}
}
func loadTransfers(ctx context.Context, accounts []common.Address, block *Block, db *Database, chainClient *chain.Client, limit int, blocksByAddress map[common.Address][]*big.Int) (map[common.Address][]Transfer, error) {
func loadTransfers(ctx context.Context, accounts []common.Address, block *Block, db *Database, chainClient *chain.ClientWithFallback, limit int, blocksByAddress map[common.Address][]*big.Int) (map[common.Address][]Transfer, error) {
start := time.Now()
group := async.NewGroup(ctx)
@ -635,7 +635,7 @@ func getLowestFrom(chainID uint64, to *big.Int) *big.Int {
return from
}
func findFirstRange(c context.Context, account common.Address, initialTo *big.Int, client *chain.Client) (*big.Int, error) {
func findFirstRange(c context.Context, account common.Address, initialTo *big.Int, client *chain.ClientWithFallback) (*big.Int, error) {
from := getLowestFrom(client.ChainID, initialTo)
to := initialTo
goal := uint64(20)
@ -691,7 +691,7 @@ func findFirstRange(c context.Context, account common.Address, initialTo *big.In
return from, nil
}
func findFirstRanges(c context.Context, accounts []common.Address, initialTo *big.Int, client *chain.Client) (map[common.Address]*big.Int, error) {
func findFirstRanges(c context.Context, accounts []common.Address, initialTo *big.Int, client *chain.ClientWithFallback) (map[common.Address]*big.Int, error) {
res := map[common.Address]*big.Int{}
for _, address := range accounts {

View File

@ -11,8 +11,8 @@ import (
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/rpc"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/services/wallet/walletevent"
)
@ -55,7 +55,7 @@ func (c *Controller) Stop() {
}
func (c *Controller) SetInitialBlocksRange(chainIDs []uint64) error {
chainClients, err := chain.NewClients(c.rpcClient, chainIDs)
chainClients, err := c.rpcClient.EthClients(chainIDs)
if err != nil {
return err
}
@ -86,7 +86,7 @@ func (c *Controller) CheckRecentHistory(chainIDs []uint64, accounts []common.Add
return err
}
chainClients, err := chain.NewClients(c.rpcClient, chainIDs)
chainClients, err := c.rpcClient.EthClients(chainIDs)
if err != nil {
return err
}
@ -116,7 +116,7 @@ func (c *Controller) CheckRecentHistory(chainIDs []uint64, accounts []common.Add
// watchAccountsChanges subsribes to a feed and watches for changes in accounts list. If there are new or removed accounts
// reactor will be restarted.
func watchAccountsChanges(ctx context.Context, accountFeed *event.Feed, reactor *Reactor, chainClients []*chain.Client, initial []common.Address) error {
func watchAccountsChanges(ctx context.Context, accountFeed *event.Feed, reactor *Reactor, chainClients []*chain.ClientWithFallback, initial []common.Address) error {
accounts := make(chan []*accounts.Account, 1) // it may block if the rate of updates will be significantly higher
sub := accountFeed.Subscribe(accounts)
defer sub.Unsubscribe()
@ -164,7 +164,7 @@ func mapToList(m map[common.Address]struct{}) []common.Address {
}
func (c *Controller) LoadTransferByHash(ctx context.Context, rpcClient *rpc.Client, address common.Address, hash common.Hash) error {
chainClient, err := chain.NewClient(rpcClient, rpcClient.UpstreamChainID)
chainClient, err := rpcClient.EthClient(rpcClient.UpstreamChainID)
if err != nil {
return err
}
@ -202,7 +202,7 @@ func (c *Controller) GetTransfersByAddress(ctx context.Context, chainID uint64,
}
transfersCount := int64(len(rst))
chainClient, err := chain.NewClient(c.rpcClient, chainID)
chainClient, err := c.rpcClient.EthClient(c.rpcClient.UpstreamChainID)
if err != nil {
return nil, err
}

View File

@ -13,7 +13,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/rpc/chain"
)
// Type type of the asset that was transferred.
@ -53,7 +53,7 @@ type Transfer struct {
// ETHDownloader downloads regular eth transfers.
type ETHDownloader struct {
chainClient *chain.Client
chainClient *chain.ClientWithFallback
accounts []common.Address
signer types.Signer
db *Database
@ -92,7 +92,7 @@ func (d *ETHDownloader) GetTransfersByNumber(ctx context.Context, number *big.In
return rst, err
}
func getTransferByHash(ctx context.Context, client *chain.Client, signer types.Signer, address common.Address, hash common.Hash) (*Transfer, error) {
func getTransferByHash(ctx context.Context, client *chain.ClientWithFallback, signer types.Signer, address common.Address, hash common.Hash) (*Transfer, error) {
transaction, _, err := client.TransactionByHash(ctx, hash)
if err != nil {
return nil, err
@ -202,7 +202,7 @@ func (d *ETHDownloader) getTransfersInBlock(ctx context.Context, blk *types.Bloc
}
// NewERC20TransfersDownloader returns new instance.
func NewERC20TransfersDownloader(client *chain.Client, accounts []common.Address, signer types.Signer) *ERC20TransfersDownloader {
func NewERC20TransfersDownloader(client *chain.ClientWithFallback, accounts []common.Address, signer types.Signer) *ERC20TransfersDownloader {
signature := crypto.Keccak256Hash([]byte(erc20TransferEventSignature))
return &ERC20TransfersDownloader{
client: client,
@ -214,7 +214,7 @@ func NewERC20TransfersDownloader(client *chain.Client, accounts []common.Address
// ERC20TransfersDownloader is a downloader for erc20 tokens transfers.
type ERC20TransfersDownloader struct {
client *chain.Client
client *chain.ClientWithFallback
accounts []common.Address
// hash of the Transfer event signature

View File

@ -9,8 +9,8 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/status-im/status-go/rpc/chain"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/chain"
)
var errAlreadyRunning = errors.New("already running")
@ -37,7 +37,7 @@ type Reactor struct {
group *async.Group
}
func (r *Reactor) newControlCommand(chainClient *chain.Client, accounts []common.Address) *controlCommand {
func (r *Reactor) newControlCommand(chainClient *chain.ClientWithFallback, accounts []common.Address) *controlCommand {
signer := types.NewLondonSigner(chainClient.ToBigInt())
ctl := &controlCommand{
db: r.db,
@ -59,7 +59,7 @@ func (r *Reactor) newControlCommand(chainClient *chain.Client, accounts []common
}
// Start runs reactor loop in background.
func (r *Reactor) start(chainClients []*chain.Client, accounts []common.Address) error {
func (r *Reactor) start(chainClients []*chain.ClientWithFallback, accounts []common.Address) error {
r.mu.Lock()
defer r.mu.Unlock()
@ -86,7 +86,7 @@ func (r *Reactor) stop() {
r.group = nil
}
func (r *Reactor) restart(chainClients []*chain.Client, accounts []common.Address) error {
func (r *Reactor) restart(chainClients []*chain.ClientWithFallback, accounts []common.Address) error {
r.stop()
return r.start(chainClients, accounts)
}

20
vendor/github.com/afex/hystrix-go/LICENSE generated vendored Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright (c) 2013 keith
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

196
vendor/github.com/afex/hystrix-go/hystrix/circuit.go generated vendored Normal file
View File

@ -0,0 +1,196 @@
package hystrix
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
// CircuitBreaker is created for each ExecutorPool to track whether requests
// should be attempted, or rejected if the Health of the circuit is too low.
type CircuitBreaker struct {
Name string
open bool
forceOpen bool
mutex *sync.RWMutex
openedOrLastTestedTime int64
executorPool *executorPool
metrics *metricExchange
}
var (
circuitBreakersMutex *sync.RWMutex
circuitBreakers map[string]*CircuitBreaker
)
func init() {
circuitBreakersMutex = &sync.RWMutex{}
circuitBreakers = make(map[string]*CircuitBreaker)
}
// GetCircuit returns the circuit for the given command and whether this call created it.
func GetCircuit(name string) (*CircuitBreaker, bool, error) {
circuitBreakersMutex.RLock()
_, ok := circuitBreakers[name]
if !ok {
circuitBreakersMutex.RUnlock()
circuitBreakersMutex.Lock()
defer circuitBreakersMutex.Unlock()
// because we released the rlock before we obtained the exclusive lock,
// we need to double check that some other thread didn't beat us to
// creation.
if cb, ok := circuitBreakers[name]; ok {
return cb, false, nil
}
circuitBreakers[name] = newCircuitBreaker(name)
} else {
defer circuitBreakersMutex.RUnlock()
}
return circuitBreakers[name], !ok, nil
}
// Flush purges all circuit and metric information from memory.
func Flush() {
circuitBreakersMutex.Lock()
defer circuitBreakersMutex.Unlock()
for name, cb := range circuitBreakers {
cb.metrics.Reset()
cb.executorPool.Metrics.Reset()
delete(circuitBreakers, name)
}
}
// newCircuitBreaker creates a CircuitBreaker with associated Health
func newCircuitBreaker(name string) *CircuitBreaker {
c := &CircuitBreaker{}
c.Name = name
c.metrics = newMetricExchange(name)
c.executorPool = newExecutorPool(name)
c.mutex = &sync.RWMutex{}
return c
}
// toggleForceOpen allows manually causing the fallback logic for all instances
// of a given command.
func (circuit *CircuitBreaker) toggleForceOpen(toggle bool) error {
circuit, _, err := GetCircuit(circuit.Name)
if err != nil {
return err
}
circuit.forceOpen = toggle
return nil
}
// IsOpen is called before any Command execution to check whether or
// not it should be attempted. An "open" circuit means it is disabled.
func (circuit *CircuitBreaker) IsOpen() bool {
circuit.mutex.RLock()
o := circuit.forceOpen || circuit.open
circuit.mutex.RUnlock()
if o {
return true
}
if uint64(circuit.metrics.Requests().Sum(time.Now())) < getSettings(circuit.Name).RequestVolumeThreshold {
return false
}
if !circuit.metrics.IsHealthy(time.Now()) {
// too many failures, open the circuit
circuit.setOpen()
return true
}
return false
}
// AllowRequest is checked before a command executes, ensuring that circuit state and metric health allow it.
// When the circuit is open, this call will occasionally return true to measure whether the external service
// has recovered.
func (circuit *CircuitBreaker) AllowRequest() bool {
return !circuit.IsOpen() || circuit.allowSingleTest()
}
func (circuit *CircuitBreaker) allowSingleTest() bool {
circuit.mutex.RLock()
defer circuit.mutex.RUnlock()
now := time.Now().UnixNano()
openedOrLastTestedTime := atomic.LoadInt64(&circuit.openedOrLastTestedTime)
if circuit.open && now > openedOrLastTestedTime+getSettings(circuit.Name).SleepWindow.Nanoseconds() {
swapped := atomic.CompareAndSwapInt64(&circuit.openedOrLastTestedTime, openedOrLastTestedTime, now)
if swapped {
log.Printf("hystrix-go: allowing single test to possibly close circuit %v", circuit.Name)
}
return swapped
}
return false
}
func (circuit *CircuitBreaker) setOpen() {
circuit.mutex.Lock()
defer circuit.mutex.Unlock()
if circuit.open {
return
}
log.Printf("hystrix-go: opening circuit %v", circuit.Name)
circuit.openedOrLastTestedTime = time.Now().UnixNano()
circuit.open = true
}
func (circuit *CircuitBreaker) setClose() {
circuit.mutex.Lock()
defer circuit.mutex.Unlock()
if !circuit.open {
return
}
log.Printf("hystrix-go: closing circuit %v", circuit.Name)
circuit.open = false
circuit.metrics.Reset()
}
// ReportEvent records command metrics for tracking recent error rates and exposing data to the dashboard.
func (circuit *CircuitBreaker) ReportEvent(eventTypes []string, start time.Time, runDuration time.Duration) error {
if len(eventTypes) == 0 {
return fmt.Errorf("no event types sent for metrics")
}
circuit.mutex.RLock()
o := circuit.open
circuit.mutex.RUnlock()
if eventTypes[0] == "success" && o {
circuit.setClose()
}
var concurrencyInUse float64
if circuit.executorPool.Max > 0 {
concurrencyInUse = float64(circuit.executorPool.ActiveCount()) / float64(circuit.executorPool.Max)
}
select {
case circuit.metrics.Updates <- &commandExecution{
Types: eventTypes,
Start: start,
RunDuration: runDuration,
ConcurrencyInUse: concurrencyInUse,
}:
default:
return CircuitError{Message: fmt.Sprintf("metrics channel (%v) is at capacity", circuit.Name)}
}
return nil
}

79
vendor/github.com/afex/hystrix-go/hystrix/doc.go generated vendored Normal file
View File

@ -0,0 +1,79 @@
/*
Package hystrix is a latency and fault tolerance library designed to isolate
points of access to remote systems, services and 3rd party libraries, stop
cascading failure and enable resilience in complex distributed systems where
failure is inevitable.
Based on the java project of the same name, by Netflix. https://github.com/Netflix/Hystrix
Execute code as a Hystrix command
Define your application logic which relies on external systems, passing your function to Go. When that system is healthy this will be the only thing which executes.
hystrix.Go("my_command", func() error {
// talk to other services
return nil
}, nil)
Defining fallback behavior
If you want code to execute during a service outage, pass in a second function to Go. Ideally, the logic here will allow your application to gracefully handle external services being unavailable.
This triggers when your code returns an error, or whenever it is unable to complete based on a variety of health checks https://github.com/Netflix/Hystrix/wiki/How-it-Works.
hystrix.Go("my_command", func() error {
// talk to other services
return nil
}, func(err error) error {
// do this when services are down
return nil
})
Waiting for output
Calling Go is like launching a goroutine, except you receive a channel of errors you can choose to monitor.
output := make(chan bool, 1)
errors := hystrix.Go("my_command", func() error {
// talk to other services
output <- true
return nil
}, nil)
select {
case out := <-output:
// success
case err := <-errors:
// failure
}
Synchronous API
Since calling a command and immediately waiting for it to finish is a common pattern, a synchronous API is available with the Do function which returns a single error.
err := hystrix.Do("my_command", func() error {
// talk to other services
return nil
}, nil)
Configure settings
During application boot, you can call ConfigureCommand to tweak the settings for each command.
hystrix.ConfigureCommand("my_command", hystrix.CommandConfig{
Timeout: 1000,
MaxConcurrentRequests: 100,
ErrorPercentThreshold: 25,
})
You can also use Configure which accepts a map[string]CommandConfig.
Enable dashboard metrics
In your main.go, register the event stream HTTP handler on a port and launch it in a goroutine. Once you configure turbine for your Hystrix Dashboard https://github.com/Netflix/Hystrix/tree/master/hystrix-dashboard to start streaming events, your commands will automatically begin appearing.
hystrixStreamHandler := hystrix.NewStreamHandler()
hystrixStreamHandler.Start()
go http.ListenAndServe(net.JoinHostPort("", "81"), hystrixStreamHandler)
*/
package hystrix

View File

@ -0,0 +1,326 @@
package hystrix
import (
"bytes"
"encoding/json"
"net/http"
"sync"
"time"
"github.com/afex/hystrix-go/hystrix/rolling"
)
const (
streamEventBufferSize = 10
)
// NewStreamHandler returns a server capable of exposing dashboard metrics via HTTP.
func NewStreamHandler() *StreamHandler {
return &StreamHandler{}
}
// StreamHandler publishes metrics for each command and each pool once a second to all connected HTTP client.
type StreamHandler struct {
requests map[*http.Request]chan []byte
mu sync.RWMutex
done chan struct{}
}
// Start begins watching the in-memory circuit breakers for metrics
func (sh *StreamHandler) Start() {
sh.requests = make(map[*http.Request]chan []byte)
sh.done = make(chan struct{})
go sh.loop()
}
// Stop shuts down the metric collection routine
func (sh *StreamHandler) Stop() {
close(sh.done)
}
var _ http.Handler = (*StreamHandler)(nil)
func (sh *StreamHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
// Make sure that the writer supports flushing.
f, ok := rw.(http.Flusher)
if !ok {
http.Error(rw, "Streaming unsupported!", http.StatusInternalServerError)
return
}
events := sh.register(req)
defer sh.unregister(req)
notify := rw.(http.CloseNotifier).CloseNotify()
rw.Header().Add("Content-Type", "text/event-stream")
rw.Header().Set("Cache-Control", "no-cache")
rw.Header().Set("Connection", "keep-alive")
for {
select {
case <-notify:
// client is gone
return
case event := <-events:
_, err := rw.Write(event)
if err != nil {
return
}
f.Flush()
}
}
}
func (sh *StreamHandler) loop() {
tick := time.Tick(1 * time.Second)
for {
select {
case <-tick:
circuitBreakersMutex.RLock()
for _, cb := range circuitBreakers {
sh.publishMetrics(cb)
sh.publishThreadPools(cb.executorPool)
}
circuitBreakersMutex.RUnlock()
case <-sh.done:
return
}
}
}
func (sh *StreamHandler) publishMetrics(cb *CircuitBreaker) error {
now := time.Now()
reqCount := cb.metrics.Requests().Sum(now)
errCount := cb.metrics.DefaultCollector().Errors().Sum(now)
errPct := cb.metrics.ErrorPercent(now)
eventBytes, err := json.Marshal(&streamCmdMetric{
Type: "HystrixCommand",
Name: cb.Name,
Group: cb.Name,
Time: currentTime(),
ReportingHosts: 1,
RequestCount: uint32(reqCount),
ErrorCount: uint32(errCount),
ErrorPct: uint32(errPct),
CircuitBreakerOpen: cb.IsOpen(),
RollingCountSuccess: uint32(cb.metrics.DefaultCollector().Successes().Sum(now)),
RollingCountFailure: uint32(cb.metrics.DefaultCollector().Failures().Sum(now)),
RollingCountThreadPoolRejected: uint32(cb.metrics.DefaultCollector().Rejects().Sum(now)),
RollingCountShortCircuited: uint32(cb.metrics.DefaultCollector().ShortCircuits().Sum(now)),
RollingCountTimeout: uint32(cb.metrics.DefaultCollector().Timeouts().Sum(now)),
RollingCountFallbackSuccess: uint32(cb.metrics.DefaultCollector().FallbackSuccesses().Sum(now)),
RollingCountFallbackFailure: uint32(cb.metrics.DefaultCollector().FallbackFailures().Sum(now)),
LatencyTotal: generateLatencyTimings(cb.metrics.DefaultCollector().TotalDuration()),
LatencyTotalMean: cb.metrics.DefaultCollector().TotalDuration().Mean(),
LatencyExecute: generateLatencyTimings(cb.metrics.DefaultCollector().RunDuration()),
LatencyExecuteMean: cb.metrics.DefaultCollector().RunDuration().Mean(),
// TODO: all hard-coded values should become configurable settings, per circuit
RollingStatsWindow: 10000,
ExecutionIsolationStrategy: "THREAD",
CircuitBreakerEnabled: true,
CircuitBreakerForceClosed: false,
CircuitBreakerForceOpen: cb.forceOpen,
CircuitBreakerErrorThresholdPercent: uint32(getSettings(cb.Name).ErrorPercentThreshold),
CircuitBreakerSleepWindow: uint32(getSettings(cb.Name).SleepWindow.Seconds() * 1000),
CircuitBreakerRequestVolumeThreshold: uint32(getSettings(cb.Name).RequestVolumeThreshold),
})
if err != nil {
return err
}
err = sh.writeToRequests(eventBytes)
if err != nil {
return err
}
return nil
}
func (sh *StreamHandler) publishThreadPools(pool *executorPool) error {
now := time.Now()
eventBytes, err := json.Marshal(&streamThreadPoolMetric{
Type: "HystrixThreadPool",
Name: pool.Name,
ReportingHosts: 1,
CurrentActiveCount: uint32(pool.ActiveCount()),
CurrentTaskCount: 0,
CurrentCompletedTaskCount: 0,
RollingCountThreadsExecuted: uint32(pool.Metrics.Executed.Sum(now)),
RollingMaxActiveThreads: uint32(pool.Metrics.MaxActiveRequests.Max(now)),
CurrentPoolSize: uint32(pool.Max),
CurrentCorePoolSize: uint32(pool.Max),
CurrentLargestPoolSize: uint32(pool.Max),
CurrentMaximumPoolSize: uint32(pool.Max),
RollingStatsWindow: 10000,
QueueSizeRejectionThreshold: 0,
CurrentQueueSize: 0,
})
if err != nil {
return err
}
err = sh.writeToRequests(eventBytes)
return nil
}
func (sh *StreamHandler) writeToRequests(eventBytes []byte) error {
var b bytes.Buffer
_, err := b.Write([]byte("data:"))
if err != nil {
return err
}
_, err = b.Write(eventBytes)
if err != nil {
return err
}
_, err = b.Write([]byte("\n\n"))
if err != nil {
return err
}
dataBytes := b.Bytes()
sh.mu.RLock()
for _, requestEvents := range sh.requests {
select {
case requestEvents <- dataBytes:
default:
}
}
sh.mu.RUnlock()
return nil
}
func (sh *StreamHandler) register(req *http.Request) <-chan []byte {
sh.mu.RLock()
events, ok := sh.requests[req]
sh.mu.RUnlock()
if ok {
return events
}
events = make(chan []byte, streamEventBufferSize)
sh.mu.Lock()
sh.requests[req] = events
sh.mu.Unlock()
return events
}
func (sh *StreamHandler) unregister(req *http.Request) {
sh.mu.Lock()
delete(sh.requests, req)
sh.mu.Unlock()
}
func generateLatencyTimings(r *rolling.Timing) streamCmdLatency {
return streamCmdLatency{
Timing0: r.Percentile(0),
Timing25: r.Percentile(25),
Timing50: r.Percentile(50),
Timing75: r.Percentile(75),
Timing90: r.Percentile(90),
Timing95: r.Percentile(95),
Timing99: r.Percentile(99),
Timing995: r.Percentile(99.5),
Timing100: r.Percentile(100),
}
}
type streamCmdMetric struct {
Type string `json:"type"`
Name string `json:"name"`
Group string `json:"group"`
Time int64 `json:"currentTime"`
ReportingHosts uint32 `json:"reportingHosts"`
// Health
RequestCount uint32 `json:"requestCount"`
ErrorCount uint32 `json:"errorCount"`
ErrorPct uint32 `json:"errorPercentage"`
CircuitBreakerOpen bool `json:"isCircuitBreakerOpen"`
RollingCountCollapsedRequests uint32 `json:"rollingCountCollapsedRequests"`
RollingCountExceptionsThrown uint32 `json:"rollingCountExceptionsThrown"`
RollingCountFailure uint32 `json:"rollingCountFailure"`
RollingCountFallbackFailure uint32 `json:"rollingCountFallbackFailure"`
RollingCountFallbackRejection uint32 `json:"rollingCountFallbackRejection"`
RollingCountFallbackSuccess uint32 `json:"rollingCountFallbackSuccess"`
RollingCountResponsesFromCache uint32 `json:"rollingCountResponsesFromCache"`
RollingCountSemaphoreRejected uint32 `json:"rollingCountSemaphoreRejected"`
RollingCountShortCircuited uint32 `json:"rollingCountShortCircuited"`
RollingCountSuccess uint32 `json:"rollingCountSuccess"`
RollingCountThreadPoolRejected uint32 `json:"rollingCountThreadPoolRejected"`
RollingCountTimeout uint32 `json:"rollingCountTimeout"`
CurrentConcurrentExecutionCount uint32 `json:"currentConcurrentExecutionCount"`
LatencyExecuteMean uint32 `json:"latencyExecute_mean"`
LatencyExecute streamCmdLatency `json:"latencyExecute"`
LatencyTotalMean uint32 `json:"latencyTotal_mean"`
LatencyTotal streamCmdLatency `json:"latencyTotal"`
// Properties
CircuitBreakerRequestVolumeThreshold uint32 `json:"propertyValue_circuitBreakerRequestVolumeThreshold"`
CircuitBreakerSleepWindow uint32 `json:"propertyValue_circuitBreakerSleepWindowInMilliseconds"`
CircuitBreakerErrorThresholdPercent uint32 `json:"propertyValue_circuitBreakerErrorThresholdPercentage"`
CircuitBreakerForceOpen bool `json:"propertyValue_circuitBreakerForceOpen"`
CircuitBreakerForceClosed bool `json:"propertyValue_circuitBreakerForceClosed"`
CircuitBreakerEnabled bool `json:"propertyValue_circuitBreakerEnabled"`
ExecutionIsolationStrategy string `json:"propertyValue_executionIsolationStrategy"`
ExecutionIsolationThreadTimeout uint32 `json:"propertyValue_executionIsolationThreadTimeoutInMilliseconds"`
ExecutionIsolationThreadInterruptOnTimeout bool `json:"propertyValue_executionIsolationThreadInterruptOnTimeout"`
ExecutionIsolationThreadPoolKeyOverride string `json:"propertyValue_executionIsolationThreadPoolKeyOverride"`
ExecutionIsolationSemaphoreMaxConcurrentRequests uint32 `json:"propertyValue_executionIsolationSemaphoreMaxConcurrentRequests"`
FallbackIsolationSemaphoreMaxConcurrentRequests uint32 `json:"propertyValue_fallbackIsolationSemaphoreMaxConcurrentRequests"`
RollingStatsWindow uint32 `json:"propertyValue_metricsRollingStatisticalWindowInMilliseconds"`
RequestCacheEnabled bool `json:"propertyValue_requestCacheEnabled"`
RequestLogEnabled bool `json:"propertyValue_requestLogEnabled"`
}
type streamCmdLatency struct {
Timing0 uint32 `json:"0"`
Timing25 uint32 `json:"25"`
Timing50 uint32 `json:"50"`
Timing75 uint32 `json:"75"`
Timing90 uint32 `json:"90"`
Timing95 uint32 `json:"95"`
Timing99 uint32 `json:"99"`
Timing995 uint32 `json:"99.5"`
Timing100 uint32 `json:"100"`
}
type streamThreadPoolMetric struct {
Type string `json:"type"`
Name string `json:"name"`
ReportingHosts uint32 `json:"reportingHosts"`
CurrentActiveCount uint32 `json:"currentActiveCount"`
CurrentCompletedTaskCount uint32 `json:"currentCompletedTaskCount"`
CurrentCorePoolSize uint32 `json:"currentCorePoolSize"`
CurrentLargestPoolSize uint32 `json:"currentLargestPoolSize"`
CurrentMaximumPoolSize uint32 `json:"currentMaximumPoolSize"`
CurrentPoolSize uint32 `json:"currentPoolSize"`
CurrentQueueSize uint32 `json:"currentQueueSize"`
CurrentTaskCount uint32 `json:"currentTaskCount"`
RollingMaxActiveThreads uint32 `json:"rollingMaxActiveThreads"`
RollingCountThreadsExecuted uint32 `json:"rollingCountThreadsExecuted"`
RollingStatsWindow uint32 `json:"propertyValue_metricsRollingStatisticalWindowInMilliseconds"`
QueueSizeRejectionThreshold uint32 `json:"propertyValue_queueSizeRejectionThreshold"`
}
func currentTime() int64 {
return time.Now().UnixNano() / int64(1000000)
}

299
vendor/github.com/afex/hystrix-go/hystrix/hystrix.go generated vendored Normal file
View File

@ -0,0 +1,299 @@
package hystrix
import (
"context"
"fmt"
"sync"
"time"
)
type runFunc func() error
type fallbackFunc func(error) error
type runFuncC func(context.Context) error
type fallbackFuncC func(context.Context, error) error
// A CircuitError is an error which models various failure states of execution,
// such as the circuit being open or a timeout.
type CircuitError struct {
Message string
}
func (e CircuitError) Error() string {
return "hystrix: " + e.Message
}
// command models the state used for a single execution on a circuit. "hystrix command" is commonly
// used to describe the pairing of your run/fallback functions with a circuit.
type command struct {
sync.Mutex
ticket *struct{}
start time.Time
errChan chan error
finished chan bool
circuit *CircuitBreaker
run runFuncC
fallback fallbackFuncC
runDuration time.Duration
events []string
}
var (
// ErrMaxConcurrency occurs when too many of the same named command are executed at the same time.
ErrMaxConcurrency = CircuitError{Message: "max concurrency"}
// ErrCircuitOpen returns when an execution attempt "short circuits". This happens due to the circuit being measured as unhealthy.
ErrCircuitOpen = CircuitError{Message: "circuit open"}
// ErrTimeout occurs when the provided function takes too long to execute.
ErrTimeout = CircuitError{Message: "timeout"}
)
// Go runs your function while tracking the health of previous calls to it.
// If your function begins slowing down or failing repeatedly, we will block
// new calls to it for you to give the dependent service time to repair.
//
// Define a fallback function if you want to define some code to execute during outages.
func Go(name string, run runFunc, fallback fallbackFunc) chan error {
runC := func(ctx context.Context) error {
return run()
}
var fallbackC fallbackFuncC
if fallback != nil {
fallbackC = func(ctx context.Context, err error) error {
return fallback(err)
}
}
return GoC(context.Background(), name, runC, fallbackC)
}
// GoC runs your function while tracking the health of previous calls to it.
// If your function begins slowing down or failing repeatedly, we will block
// new calls to it for you to give the dependent service time to repair.
//
// Define a fallback function if you want to define some code to execute during outages.
func GoC(ctx context.Context, name string, run runFuncC, fallback fallbackFuncC) chan error {
cmd := &command{
run: run,
fallback: fallback,
start: time.Now(),
errChan: make(chan error, 1),
finished: make(chan bool, 1),
}
// dont have methods with explicit params and returns
// let data come in and out naturally, like with any closure
// explicit error return to give place for us to kill switch the operation (fallback)
circuit, _, err := GetCircuit(name)
if err != nil {
cmd.errChan <- err
return cmd.errChan
}
cmd.circuit = circuit
ticketCond := sync.NewCond(cmd)
ticketChecked := false
// When the caller extracts error from returned errChan, it's assumed that
// the ticket's been returned to executorPool. Therefore, returnTicket() can
// not run after cmd.errorWithFallback().
returnTicket := func() {
cmd.Lock()
// Avoid releasing before a ticket is acquired.
for !ticketChecked {
ticketCond.Wait()
}
cmd.circuit.executorPool.Return(cmd.ticket)
cmd.Unlock()
}
// Shared by the following two goroutines. It ensures only the faster
// goroutine runs errWithFallback() and reportAllEvent().
returnOnce := &sync.Once{}
reportAllEvent := func() {
err := cmd.circuit.ReportEvent(cmd.events, cmd.start, cmd.runDuration)
if err != nil {
log.Printf(err.Error())
}
}
go func() {
defer func() { cmd.finished <- true }()
// Circuits get opened when recent executions have shown to have a high error rate.
// Rejecting new executions allows backends to recover, and the circuit will allow
// new traffic when it feels a healthly state has returned.
if !cmd.circuit.AllowRequest() {
cmd.Lock()
// It's safe for another goroutine to go ahead releasing a nil ticket.
ticketChecked = true
ticketCond.Signal()
cmd.Unlock()
returnOnce.Do(func() {
returnTicket()
cmd.errorWithFallback(ctx, ErrCircuitOpen)
reportAllEvent()
})
return
}
// As backends falter, requests take longer but don't always fail.
//
// When requests slow down but the incoming rate of requests stays the same, you have to
// run more at a time to keep up. By controlling concurrency during these situations, you can
// shed load which accumulates due to the increasing ratio of active commands to incoming requests.
cmd.Lock()
select {
case cmd.ticket = <-circuit.executorPool.Tickets:
ticketChecked = true
ticketCond.Signal()
cmd.Unlock()
default:
ticketChecked = true
ticketCond.Signal()
cmd.Unlock()
returnOnce.Do(func() {
returnTicket()
cmd.errorWithFallback(ctx, ErrMaxConcurrency)
reportAllEvent()
})
return
}
runStart := time.Now()
runErr := run(ctx)
returnOnce.Do(func() {
defer reportAllEvent()
cmd.runDuration = time.Since(runStart)
returnTicket()
if runErr != nil {
cmd.errorWithFallback(ctx, runErr)
return
}
cmd.reportEvent("success")
})
}()
go func() {
timer := time.NewTimer(getSettings(name).Timeout)
defer timer.Stop()
select {
case <-cmd.finished:
// returnOnce has been executed in another goroutine
case <-ctx.Done():
returnOnce.Do(func() {
returnTicket()
cmd.errorWithFallback(ctx, ctx.Err())
reportAllEvent()
})
return
case <-timer.C:
returnOnce.Do(func() {
returnTicket()
cmd.errorWithFallback(ctx, ErrTimeout)
reportAllEvent()
})
return
}
}()
return cmd.errChan
}
// Do runs your function in a synchronous manner, blocking until either your function succeeds
// or an error is returned, including hystrix circuit errors
func Do(name string, run runFunc, fallback fallbackFunc) error {
runC := func(ctx context.Context) error {
return run()
}
var fallbackC fallbackFuncC
if fallback != nil {
fallbackC = func(ctx context.Context, err error) error {
return fallback(err)
}
}
return DoC(context.Background(), name, runC, fallbackC)
}
// DoC runs your function in a synchronous manner, blocking until either your function succeeds
// or an error is returned, including hystrix circuit errors
func DoC(ctx context.Context, name string, run runFuncC, fallback fallbackFuncC) error {
done := make(chan struct{}, 1)
r := func(ctx context.Context) error {
err := run(ctx)
if err != nil {
return err
}
done <- struct{}{}
return nil
}
f := func(ctx context.Context, e error) error {
err := fallback(ctx, e)
if err != nil {
return err
}
done <- struct{}{}
return nil
}
var errChan chan error
if fallback == nil {
errChan = GoC(ctx, name, r, nil)
} else {
errChan = GoC(ctx, name, r, f)
}
select {
case <-done:
return nil
case err := <-errChan:
return err
}
}
func (c *command) reportEvent(eventType string) {
c.Lock()
defer c.Unlock()
c.events = append(c.events, eventType)
}
// errorWithFallback triggers the fallback while reporting the appropriate metric events.
func (c *command) errorWithFallback(ctx context.Context, err error) {
eventType := "failure"
if err == ErrCircuitOpen {
eventType = "short-circuit"
} else if err == ErrMaxConcurrency {
eventType = "rejected"
} else if err == ErrTimeout {
eventType = "timeout"
} else if err == context.Canceled {
eventType = "context_canceled"
} else if err == context.DeadlineExceeded {
eventType = "context_deadline_exceeded"
}
c.reportEvent(eventType)
fallbackErr := c.tryFallback(ctx, err)
if fallbackErr != nil {
c.errChan <- fallbackErr
}
}
func (c *command) tryFallback(ctx context.Context, err error) error {
if c.fallback == nil {
// If we don't have a fallback return the original error.
return err
}
fallbackErr := c.fallback(ctx, err)
if fallbackErr != nil {
c.reportEvent("fallback-failure")
return fmt.Errorf("fallback failed with '%v'. run error was '%v'", fallbackErr, err)
}
c.reportEvent("fallback-success")
return nil
}

11
vendor/github.com/afex/hystrix-go/hystrix/logger.go generated vendored Normal file
View File

@ -0,0 +1,11 @@
package hystrix
type logger interface {
Printf(format string, items ...interface{})
}
// NoopLogger does not log anything.
type NoopLogger struct{}
// Printf does nothing.
func (l NoopLogger) Printf(format string, items ...interface{}) {}

View File

@ -0,0 +1,169 @@
package metricCollector
import (
"sync"
"github.com/afex/hystrix-go/hystrix/rolling"
)
// DefaultMetricCollector holds information about the circuit state.
// This implementation of MetricCollector is the canonical source of information about the circuit.
// It is used for for all internal hystrix operations
// including circuit health checks and metrics sent to the hystrix dashboard.
//
// Metric Collectors do not need Mutexes as they are updated by circuits within a locked context.
type DefaultMetricCollector struct {
mutex *sync.RWMutex
numRequests *rolling.Number
errors *rolling.Number
successes *rolling.Number
failures *rolling.Number
rejects *rolling.Number
shortCircuits *rolling.Number
timeouts *rolling.Number
contextCanceled *rolling.Number
contextDeadlineExceeded *rolling.Number
fallbackSuccesses *rolling.Number
fallbackFailures *rolling.Number
totalDuration *rolling.Timing
runDuration *rolling.Timing
}
func newDefaultMetricCollector(name string) MetricCollector {
m := &DefaultMetricCollector{}
m.mutex = &sync.RWMutex{}
m.Reset()
return m
}
// NumRequests returns the rolling number of requests
func (d *DefaultMetricCollector) NumRequests() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.numRequests
}
// Errors returns the rolling number of errors
func (d *DefaultMetricCollector) Errors() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.errors
}
// Successes returns the rolling number of successes
func (d *DefaultMetricCollector) Successes() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.successes
}
// Failures returns the rolling number of failures
func (d *DefaultMetricCollector) Failures() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.failures
}
// Rejects returns the rolling number of rejects
func (d *DefaultMetricCollector) Rejects() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.rejects
}
// ShortCircuits returns the rolling number of short circuits
func (d *DefaultMetricCollector) ShortCircuits() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.shortCircuits
}
// Timeouts returns the rolling number of timeouts
func (d *DefaultMetricCollector) Timeouts() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.timeouts
}
// FallbackSuccesses returns the rolling number of fallback successes
func (d *DefaultMetricCollector) FallbackSuccesses() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.fallbackSuccesses
}
func (d *DefaultMetricCollector) ContextCanceled() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.contextCanceled
}
func (d *DefaultMetricCollector) ContextDeadlineExceeded() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.contextDeadlineExceeded
}
// FallbackFailures returns the rolling number of fallback failures
func (d *DefaultMetricCollector) FallbackFailures() *rolling.Number {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.fallbackFailures
}
// TotalDuration returns the rolling total duration
func (d *DefaultMetricCollector) TotalDuration() *rolling.Timing {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.totalDuration
}
// RunDuration returns the rolling run duration
func (d *DefaultMetricCollector) RunDuration() *rolling.Timing {
d.mutex.RLock()
defer d.mutex.RUnlock()
return d.runDuration
}
func (d *DefaultMetricCollector) Update(r MetricResult) {
d.mutex.RLock()
defer d.mutex.RUnlock()
d.numRequests.Increment(r.Attempts)
d.errors.Increment(r.Errors)
d.successes.Increment(r.Successes)
d.failures.Increment(r.Failures)
d.rejects.Increment(r.Rejects)
d.shortCircuits.Increment(r.ShortCircuits)
d.timeouts.Increment(r.Timeouts)
d.fallbackSuccesses.Increment(r.FallbackSuccesses)
d.fallbackFailures.Increment(r.FallbackFailures)
d.contextCanceled.Increment(r.ContextCanceled)
d.contextDeadlineExceeded.Increment(r.ContextDeadlineExceeded)
d.totalDuration.Add(r.TotalDuration)
d.runDuration.Add(r.RunDuration)
}
// Reset resets all metrics in this collector to 0.
func (d *DefaultMetricCollector) Reset() {
d.mutex.Lock()
defer d.mutex.Unlock()
d.numRequests = rolling.NewNumber()
d.errors = rolling.NewNumber()
d.successes = rolling.NewNumber()
d.rejects = rolling.NewNumber()
d.shortCircuits = rolling.NewNumber()
d.failures = rolling.NewNumber()
d.timeouts = rolling.NewNumber()
d.fallbackSuccesses = rolling.NewNumber()
d.fallbackFailures = rolling.NewNumber()
d.contextCanceled = rolling.NewNumber()
d.contextDeadlineExceeded = rolling.NewNumber()
d.totalDuration = rolling.NewTiming()
d.runDuration = rolling.NewTiming()
}

View File

@ -0,0 +1,67 @@
package metricCollector
import (
"sync"
"time"
)
// Registry is the default metricCollectorRegistry that circuits will use to
// collect statistics about the health of the circuit.
var Registry = metricCollectorRegistry{
lock: &sync.RWMutex{},
registry: []func(name string) MetricCollector{
newDefaultMetricCollector,
},
}
type metricCollectorRegistry struct {
lock *sync.RWMutex
registry []func(name string) MetricCollector
}
// InitializeMetricCollectors runs the registried MetricCollector Initializers to create an array of MetricCollectors.
func (m *metricCollectorRegistry) InitializeMetricCollectors(name string) []MetricCollector {
m.lock.RLock()
defer m.lock.RUnlock()
metrics := make([]MetricCollector, len(m.registry))
for i, metricCollectorInitializer := range m.registry {
metrics[i] = metricCollectorInitializer(name)
}
return metrics
}
// Register places a MetricCollector Initializer in the registry maintained by this metricCollectorRegistry.
func (m *metricCollectorRegistry) Register(initMetricCollector func(string) MetricCollector) {
m.lock.Lock()
defer m.lock.Unlock()
m.registry = append(m.registry, initMetricCollector)
}
type MetricResult struct {
Attempts float64
Errors float64
Successes float64
Failures float64
Rejects float64
ShortCircuits float64
Timeouts float64
FallbackSuccesses float64
FallbackFailures float64
ContextCanceled float64
ContextDeadlineExceeded float64
TotalDuration time.Duration
RunDuration time.Duration
ConcurrencyInUse float64
}
// MetricCollector represents the contract that all collectors must fulfill to gather circuit statistics.
// Implementations of this interface do not have to maintain locking around thier data stores so long as
// they are not modified outside of the hystrix context.
type MetricCollector interface {
// Update accepts a set of metrics from a command execution for remote instrumentation
Update(MetricResult)
// Reset resets the internal counters and timers.
Reset()
}

150
vendor/github.com/afex/hystrix-go/hystrix/metrics.go generated vendored Normal file
View File

@ -0,0 +1,150 @@
package hystrix
import (
"sync"
"time"
"github.com/afex/hystrix-go/hystrix/metric_collector"
"github.com/afex/hystrix-go/hystrix/rolling"
)
type commandExecution struct {
Types []string `json:"types"`
Start time.Time `json:"start_time"`
RunDuration time.Duration `json:"run_duration"`
ConcurrencyInUse float64 `json:"concurrency_inuse"`
}
type metricExchange struct {
Name string
Updates chan *commandExecution
Mutex *sync.RWMutex
metricCollectors []metricCollector.MetricCollector
}
func newMetricExchange(name string) *metricExchange {
m := &metricExchange{}
m.Name = name
m.Updates = make(chan *commandExecution, 2000)
m.Mutex = &sync.RWMutex{}
m.metricCollectors = metricCollector.Registry.InitializeMetricCollectors(name)
m.Reset()
go m.Monitor()
return m
}
// The Default Collector function will panic if collectors are not setup to specification.
func (m *metricExchange) DefaultCollector() *metricCollector.DefaultMetricCollector {
if len(m.metricCollectors) < 1 {
panic("No Metric Collectors Registered.")
}
collection, ok := m.metricCollectors[0].(*metricCollector.DefaultMetricCollector)
if !ok {
panic("Default metric collector is not registered correctly. The default metric collector must be registered first.")
}
return collection
}
func (m *metricExchange) Monitor() {
for update := range m.Updates {
// we only grab a read lock to make sure Reset() isn't changing the numbers.
m.Mutex.RLock()
totalDuration := time.Since(update.Start)
wg := &sync.WaitGroup{}
for _, collector := range m.metricCollectors {
wg.Add(1)
go m.IncrementMetrics(wg, collector, update, totalDuration)
}
wg.Wait()
m.Mutex.RUnlock()
}
}
func (m *metricExchange) IncrementMetrics(wg *sync.WaitGroup, collector metricCollector.MetricCollector, update *commandExecution, totalDuration time.Duration) {
// granular metrics
r := metricCollector.MetricResult{
Attempts: 1,
TotalDuration: totalDuration,
RunDuration: update.RunDuration,
ConcurrencyInUse: update.ConcurrencyInUse,
}
switch update.Types[0] {
case "success":
r.Successes = 1
case "failure":
r.Failures = 1
r.Errors = 1
case "rejected":
r.Rejects = 1
r.Errors = 1
case "short-circuit":
r.ShortCircuits = 1
r.Errors = 1
case "timeout":
r.Timeouts = 1
r.Errors = 1
case "context_canceled":
r.ContextCanceled = 1
case "context_deadline_exceeded":
r.ContextDeadlineExceeded = 1
}
if len(update.Types) > 1 {
// fallback metrics
if update.Types[1] == "fallback-success" {
r.FallbackSuccesses = 1
}
if update.Types[1] == "fallback-failure" {
r.FallbackFailures = 1
}
}
collector.Update(r)
wg.Done()
}
func (m *metricExchange) Reset() {
m.Mutex.Lock()
defer m.Mutex.Unlock()
for _, collector := range m.metricCollectors {
collector.Reset()
}
}
func (m *metricExchange) Requests() *rolling.Number {
m.Mutex.RLock()
defer m.Mutex.RUnlock()
return m.requestsLocked()
}
func (m *metricExchange) requestsLocked() *rolling.Number {
return m.DefaultCollector().NumRequests()
}
func (m *metricExchange) ErrorPercent(now time.Time) int {
m.Mutex.RLock()
defer m.Mutex.RUnlock()
var errPct float64
reqs := m.requestsLocked().Sum(now)
errs := m.DefaultCollector().Errors().Sum(now)
if reqs > 0 {
errPct = (float64(errs) / float64(reqs)) * 100
}
return int(errPct + 0.5)
}
func (m *metricExchange) IsHealthy(now time.Time) bool {
return m.ErrorPercent(now) < getSettings(m.Name).ErrorPercentThreshold
}

37
vendor/github.com/afex/hystrix-go/hystrix/pool.go generated vendored Normal file
View File

@ -0,0 +1,37 @@
package hystrix
type executorPool struct {
Name string
Metrics *poolMetrics
Max int
Tickets chan *struct{}
}
func newExecutorPool(name string) *executorPool {
p := &executorPool{}
p.Name = name
p.Metrics = newPoolMetrics(name)
p.Max = getSettings(name).MaxConcurrentRequests
p.Tickets = make(chan *struct{}, p.Max)
for i := 0; i < p.Max; i++ {
p.Tickets <- &struct{}{}
}
return p
}
func (p *executorPool) Return(ticket *struct{}) {
if ticket == nil {
return
}
p.Metrics.Updates <- poolMetricsUpdate{
activeCount: p.ActiveCount(),
}
p.Tickets <- ticket
}
func (p *executorPool) ActiveCount() int {
return p.Max - len(p.Tickets)
}

View File

@ -0,0 +1,52 @@
package hystrix
import (
"sync"
"github.com/afex/hystrix-go/hystrix/rolling"
)
type poolMetrics struct {
Mutex *sync.RWMutex
Updates chan poolMetricsUpdate
Name string
MaxActiveRequests *rolling.Number
Executed *rolling.Number
}
type poolMetricsUpdate struct {
activeCount int
}
func newPoolMetrics(name string) *poolMetrics {
m := &poolMetrics{}
m.Name = name
m.Updates = make(chan poolMetricsUpdate)
m.Mutex = &sync.RWMutex{}
m.Reset()
go m.Monitor()
return m
}
func (m *poolMetrics) Reset() {
m.Mutex.Lock()
defer m.Mutex.Unlock()
m.MaxActiveRequests = rolling.NewNumber()
m.Executed = rolling.NewNumber()
}
func (m *poolMetrics) Monitor() {
for u := range m.Updates {
m.Mutex.RLock()
m.Executed.Increment(1)
m.MaxActiveRequests.UpdateMax(float64(u.activeCount))
m.Mutex.RUnlock()
}
}

View File

@ -0,0 +1,116 @@
package rolling
import (
"sync"
"time"
)
// Number tracks a numberBucket over a bounded number of
// time buckets. Currently the buckets are one second long and only the last 10 seconds are kept.
type Number struct {
Buckets map[int64]*numberBucket
Mutex *sync.RWMutex
}
type numberBucket struct {
Value float64
}
// NewNumber initializes a RollingNumber struct.
func NewNumber() *Number {
r := &Number{
Buckets: make(map[int64]*numberBucket),
Mutex: &sync.RWMutex{},
}
return r
}
func (r *Number) getCurrentBucket() *numberBucket {
now := time.Now().Unix()
var bucket *numberBucket
var ok bool
if bucket, ok = r.Buckets[now]; !ok {
bucket = &numberBucket{}
r.Buckets[now] = bucket
}
return bucket
}
func (r *Number) removeOldBuckets() {
now := time.Now().Unix() - 10
for timestamp := range r.Buckets {
// TODO: configurable rolling window
if timestamp <= now {
delete(r.Buckets, timestamp)
}
}
}
// Increment increments the number in current timeBucket.
func (r *Number) Increment(i float64) {
if i == 0 {
return
}
r.Mutex.Lock()
defer r.Mutex.Unlock()
b := r.getCurrentBucket()
b.Value += i
r.removeOldBuckets()
}
// UpdateMax updates the maximum value in the current bucket.
func (r *Number) UpdateMax(n float64) {
r.Mutex.Lock()
defer r.Mutex.Unlock()
b := r.getCurrentBucket()
if n > b.Value {
b.Value = n
}
r.removeOldBuckets()
}
// Sum sums the values over the buckets in the last 10 seconds.
func (r *Number) Sum(now time.Time) float64 {
sum := float64(0)
r.Mutex.RLock()
defer r.Mutex.RUnlock()
for timestamp, bucket := range r.Buckets {
// TODO: configurable rolling window
if timestamp >= now.Unix()-10 {
sum += bucket.Value
}
}
return sum
}
// Max returns the maximum value seen in the last 10 seconds.
func (r *Number) Max(now time.Time) float64 {
var max float64
r.Mutex.RLock()
defer r.Mutex.RUnlock()
for timestamp, bucket := range r.Buckets {
// TODO: configurable rolling window
if timestamp >= now.Unix()-10 {
if bucket.Value > max {
max = bucket.Value
}
}
}
return max
}
func (r *Number) Avg(now time.Time) float64 {
return r.Sum(now) / 10
}

View File

@ -0,0 +1,148 @@
package rolling
import (
"math"
"sort"
"sync"
"time"
)
// Timing maintains time Durations for each time bucket.
// The Durations are kept in an array to allow for a variety of
// statistics to be calculated from the source data.
type Timing struct {
Buckets map[int64]*timingBucket
Mutex *sync.RWMutex
CachedSortedDurations []time.Duration
LastCachedTime int64
}
type timingBucket struct {
Durations []time.Duration
}
// NewTiming creates a RollingTiming struct.
func NewTiming() *Timing {
r := &Timing{
Buckets: make(map[int64]*timingBucket),
Mutex: &sync.RWMutex{},
}
return r
}
type byDuration []time.Duration
func (c byDuration) Len() int { return len(c) }
func (c byDuration) Swap(i, j int) { c[i], c[j] = c[j], c[i] }
func (c byDuration) Less(i, j int) bool { return c[i] < c[j] }
// SortedDurations returns an array of time.Duration sorted from shortest
// to longest that have occurred in the last 60 seconds.
func (r *Timing) SortedDurations() []time.Duration {
r.Mutex.RLock()
t := r.LastCachedTime
r.Mutex.RUnlock()
if t+time.Duration(1*time.Second).Nanoseconds() > time.Now().UnixNano() {
// don't recalculate if current cache is still fresh
return r.CachedSortedDurations
}
var durations byDuration
now := time.Now()
r.Mutex.Lock()
defer r.Mutex.Unlock()
for timestamp, b := range r.Buckets {
// TODO: configurable rolling window
if timestamp >= now.Unix()-60 {
for _, d := range b.Durations {
durations = append(durations, d)
}
}
}
sort.Sort(durations)
r.CachedSortedDurations = durations
r.LastCachedTime = time.Now().UnixNano()
return r.CachedSortedDurations
}
func (r *Timing) getCurrentBucket() *timingBucket {
r.Mutex.RLock()
now := time.Now()
bucket, exists := r.Buckets[now.Unix()]
r.Mutex.RUnlock()
if !exists {
r.Mutex.Lock()
defer r.Mutex.Unlock()
r.Buckets[now.Unix()] = &timingBucket{}
bucket = r.Buckets[now.Unix()]
}
return bucket
}
func (r *Timing) removeOldBuckets() {
now := time.Now()
for timestamp := range r.Buckets {
// TODO: configurable rolling window
if timestamp <= now.Unix()-60 {
delete(r.Buckets, timestamp)
}
}
}
// Add appends the time.Duration given to the current time bucket.
func (r *Timing) Add(duration time.Duration) {
b := r.getCurrentBucket()
r.Mutex.Lock()
defer r.Mutex.Unlock()
b.Durations = append(b.Durations, duration)
r.removeOldBuckets()
}
// Percentile computes the percentile given with a linear interpolation.
func (r *Timing) Percentile(p float64) uint32 {
sortedDurations := r.SortedDurations()
length := len(sortedDurations)
if length <= 0 {
return 0
}
pos := r.ordinal(len(sortedDurations), p) - 1
return uint32(sortedDurations[pos].Nanoseconds() / 1000000)
}
func (r *Timing) ordinal(length int, percentile float64) int64 {
if percentile == 0 && length > 0 {
return 1
}
return int64(math.Ceil((percentile / float64(100)) * float64(length)))
}
// Mean computes the average timing in the last 60 seconds.
func (r *Timing) Mean() uint32 {
sortedDurations := r.SortedDurations()
var sum time.Duration
for _, d := range sortedDurations {
sum += d
}
length := int64(len(sortedDurations))
if length == 0 {
return 0
}
return uint32(sum.Nanoseconds()/length) / 1000000
}

124
vendor/github.com/afex/hystrix-go/hystrix/settings.go generated vendored Normal file
View File

@ -0,0 +1,124 @@
package hystrix
import (
"sync"
"time"
)
var (
// DefaultTimeout is how long to wait for command to complete, in milliseconds
DefaultTimeout = 1000
// DefaultMaxConcurrent is how many commands of the same type can run at the same time
DefaultMaxConcurrent = 10
// DefaultVolumeThreshold is the minimum number of requests needed before a circuit can be tripped due to health
DefaultVolumeThreshold = 20
// DefaultSleepWindow is how long, in milliseconds, to wait after a circuit opens before testing for recovery
DefaultSleepWindow = 5000
// DefaultErrorPercentThreshold causes circuits to open once the rolling measure of errors exceeds this percent of requests
DefaultErrorPercentThreshold = 50
// DefaultLogger is the default logger that will be used in the Hystrix package. By default prints nothing.
DefaultLogger = NoopLogger{}
)
type Settings struct {
Timeout time.Duration
MaxConcurrentRequests int
RequestVolumeThreshold uint64
SleepWindow time.Duration
ErrorPercentThreshold int
}
// CommandConfig is used to tune circuit settings at runtime
type CommandConfig struct {
Timeout int `json:"timeout"`
MaxConcurrentRequests int `json:"max_concurrent_requests"`
RequestVolumeThreshold int `json:"request_volume_threshold"`
SleepWindow int `json:"sleep_window"`
ErrorPercentThreshold int `json:"error_percent_threshold"`
}
var circuitSettings map[string]*Settings
var settingsMutex *sync.RWMutex
var log logger
func init() {
circuitSettings = make(map[string]*Settings)
settingsMutex = &sync.RWMutex{}
log = DefaultLogger
}
// Configure applies settings for a set of circuits
func Configure(cmds map[string]CommandConfig) {
for k, v := range cmds {
ConfigureCommand(k, v)
}
}
// ConfigureCommand applies settings for a circuit
func ConfigureCommand(name string, config CommandConfig) {
settingsMutex.Lock()
defer settingsMutex.Unlock()
timeout := DefaultTimeout
if config.Timeout != 0 {
timeout = config.Timeout
}
max := DefaultMaxConcurrent
if config.MaxConcurrentRequests != 0 {
max = config.MaxConcurrentRequests
}
volume := DefaultVolumeThreshold
if config.RequestVolumeThreshold != 0 {
volume = config.RequestVolumeThreshold
}
sleep := DefaultSleepWindow
if config.SleepWindow != 0 {
sleep = config.SleepWindow
}
errorPercent := DefaultErrorPercentThreshold
if config.ErrorPercentThreshold != 0 {
errorPercent = config.ErrorPercentThreshold
}
circuitSettings[name] = &Settings{
Timeout: time.Duration(timeout) * time.Millisecond,
MaxConcurrentRequests: max,
RequestVolumeThreshold: uint64(volume),
SleepWindow: time.Duration(sleep) * time.Millisecond,
ErrorPercentThreshold: errorPercent,
}
}
func getSettings(name string) *Settings {
settingsMutex.RLock()
s, exists := circuitSettings[name]
settingsMutex.RUnlock()
if !exists {
ConfigureCommand(name, CommandConfig{})
s = getSettings(name)
}
return s
}
func GetCircuitSettings() map[string]*Settings {
copy := make(map[string]*Settings)
settingsMutex.RLock()
for key, val := range circuitSettings {
copy[key] = val
}
settingsMutex.RUnlock()
return copy
}
// SetLogger configures the logger that will be used. This only applies to the hystrix package.
func SetLogger(l logger) {
log = l
}

5
vendor/modules.txt vendored
View File

@ -12,6 +12,11 @@ github.com/StackExchange/wmi
# github.com/VictoriaMetrics/fastcache v1.6.0
## explicit; go 1.13
github.com/VictoriaMetrics/fastcache
# github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5
## explicit
github.com/afex/hystrix-go/hystrix
github.com/afex/hystrix-go/hystrix/metric_collector
github.com/afex/hystrix-go/hystrix/rolling
# github.com/anacrolix/chansync v0.3.0
## explicit; go 1.16
github.com/anacrolix/chansync