Skip to content

Commit

Permalink
(feat) Add OFAC list check
Browse files Browse the repository at this point in the history
  • Loading branch information
shibaeff committed Sep 9, 2024
1 parent b7a68cc commit a46b970
Show file tree
Hide file tree
Showing 6 changed files with 256 additions and 5 deletions.
62 changes: 59 additions & 3 deletions client/chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ type chainClient struct {

sessionEnabled bool

ofacChecker *OfacChecker

authQueryClient authtypes.QueryClient
authzQueryClient authztypes.QueryClient
bankQueryClient banktypes.QueryClient
Expand All @@ -342,6 +344,29 @@ type chainClient struct {
canSign bool
}

//func (cc *chainClient) loadOfacList() error {
// response, err := http.Get(defaultOfacListURL)
// if err != nil {
// return err
// }
// defer response.Body.Close()
//
// if response.StatusCode != http.StatusOK {
// return fmt.Errorf("request to the OFAC upstream failed with code: %s", response.Status)
// }
//
// body, err := io.ReadAll(response.Body)
// if err != nil {
// return err
// }
//
// var ofacList []string
// if err := json.Unmarshal(body, &ofacList); err != nil {
// return err
// }
// return nil
//}

func NewChainClient(
ctx client.Context,
network common.Network,
Expand Down Expand Up @@ -440,15 +465,25 @@ func NewChainClient(
subaccountToNonce: make(map[ethcommon.Hash]uint32),
}

_ = NewTxFactory(ctx).WithSequence(0).WithAccountNumber(0).WithGas(0)

cc.ofacChecker, err = NewOfacChecker()
if err != nil {
return nil, errors.Wrap(err, "Error creating OFAC checker")
}
if cc.canSign {
var err error

cc.accNum, cc.accSeq, err = cc.txFactory.AccountRetriever().GetAccountNumberSequence(ctx, ctx.GetFromAddress())
account, err := cc.txFactory.AccountRetriever().GetAccount(ctx, ctx.GetFromAddress())
if err != nil {
err = errors.Wrap(err, "failed to get initial account num and seq")
err = errors.Wrapf(err, "failed to get account")
return nil, err
}

if cc.ofacChecker.IsBlacklisted(account.GetAddress().String()) {
return nil, errors.Errorf("Address %s is in the OFAC list", account.GetAddress())
}
cc.accNum, cc.accSeq = account.GetAccountNumber(), account.GetSequence()
go cc.runBatchBroadcast()
go cc.syncTimeoutHeight()
}
Expand Down Expand Up @@ -774,6 +809,21 @@ func (c *chainClient) BuildSignedTx(clientCtx client.Context, accNum, accSeq, in
}

func (c *chainClient) buildSignedTx(clientCtx client.Context, txf tx.Factory, msgs ...sdk.Msg) ([]byte, error) {
k, err := txf.Keybase().Key(clientCtx.FromName)
if err != nil {
err = errors.Wrap(err, "error parsing signer account address")
return nil, err
}
signerAddressPubKey, err := k.GetPubKey()
if err != nil {
err = errors.Wrap(err, "error getting signer public key")
return nil, err
}
if c.ofacChecker.IsBlacklisted(sdk.AccAddress(signerAddressPubKey.Address()).String()) {
err = errors.Errorf("Address is in the OFAC list")
return nil, err
}

ctx := context.Background()
if clientCtx.Simulate {
simTxBytes, err := txf.BuildSimTx(msgs...)
Expand All @@ -796,7 +846,7 @@ func (c *chainClient) buildSignedTx(clientCtx client.Context, txf tx.Factory, ms
c.gasWanted = adjustedGas
}

txf, err := PrepareFactory(clientCtx, txf)
txf, err = PrepareFactory(clientCtx, txf)
if err != nil {
return nil, errors.Wrap(err, "failed to prepareFactory")
}
Expand Down Expand Up @@ -1153,6 +1203,9 @@ func (c *chainClient) GetAuthzGrants(ctx context.Context, req authztypes.QueryGr
}

func (c *chainClient) BuildGenericAuthz(granter, grantee, msgtype string, expireIn time.Time) *authztypes.MsgGrant {
if c.ofacChecker.IsBlacklisted(granter) {
panic("Address is in the OFAC list") // panics should generally be avoided, but otherwise function signature should be changed
}
authz := authztypes.NewGenericAuthorization(msgtype)
authzAny := codectypes.UnsafePackAny(authz)
return &authztypes.MsgGrant{
Expand Down Expand Up @@ -1184,6 +1237,9 @@ var (
)

func (c *chainClient) BuildExchangeAuthz(granter, grantee string, authzType ExchangeAuthz, subaccountId string, markets []string, expireIn time.Time) *authztypes.MsgGrant {
if c.ofacChecker.IsBlacklisted(granter) {
panic("Address is in the OFAC list") // panics should generally be avoided, but otherwise function signature should be changed
}
var typedAuthzAny codectypes.Any
var typedAuthzBytes []byte
switch authzType {
Expand Down
47 changes: 46 additions & 1 deletion client/chain/chain_test.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package chain

import (
"encoding/json"
"github.com/stretchr/testify/require"
"io"
"os"
"testing"

"github.com/stretchr/testify/assert"

"github.com/InjectiveLabs/sdk-go/client"
"github.com/InjectiveLabs/sdk-go/client/common"
rpchttp "github.com/cometbft/cometbft/rpc/client/http"
Expand Down Expand Up @@ -51,6 +56,47 @@ func createClient(senderAddress cosmtypes.AccAddress, cosmosKeyring keyring.Keyr
return chainClient, err
}

func TestOfacList(t *testing.T) {
network := common.LoadNetwork("testnet", "lb")
tmClient, err := rpchttp.New(network.TmEndpoint, "/websocket")
assert.NoError(t, err)

senderAddress, cosmosKeyring, err := accountForTests()
assert.NoError(t, err)

testList := []string{
senderAddress.String(),
}
jsonData, err := json.Marshal(testList)
assert.NoError(t, err)
err = os.Remove(getOfacListPath())
assert.NoError(t, err)
file, err := os.Create(getOfacListPath())
_, err = io.WriteString(file, string(jsonData))
assert.NoError(t, err)
err = file.Close()
assert.NoError(t, err)
clientCtx, err := NewClientContext(
network.ChainId,
senderAddress.String(),
cosmosKeyring,
)
assert.NoError(t, err)

clientCtx = clientCtx.WithNodeURI(network.TmEndpoint).WithClient(tmClient)

_, err = NewChainClient(
clientCtx,
network,
common.OptionGasPrices(client.DefaultGasPriceWithDenom),
)
require.Error(t, err)
err = os.Remove(getOfacListPath())
assert.NoError(t, err)
err = DownloadOfacList()
assert.NoError(t, err)
}

func TestDefaultSubaccount(t *testing.T) {
network := common.LoadNetwork("devnet", "lb")
senderAddress, cosmosKeyring, err := accountForTests()
Expand Down Expand Up @@ -103,5 +149,4 @@ func TestGetSubaccountWithIndex(t *testing.T) {
if subaccountThirty != expectedSubaccountThirtyIdHash {
t.Error("The subaccount with index 30 was calculated incorrectly")
}

}
90 changes: 90 additions & 0 deletions client/chain/ofac.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package chain

import (
"encoding/json"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
)

const (
defaultOfacListURL = "https://raw.githubusercontent.com/InjectiveLabs/injective-lists/master/wallets/ofac.json"
defaultofacListFilename = "ofac.json"
)

type OfacChecker struct {
ofacListPath string
ofacList []string
}

func NewOfacChecker() (*OfacChecker, error) {
checker := &OfacChecker{
ofacListPath: getOfacListPath(),
}
if _, err := os.Stat(checker.ofacListPath); os.IsNotExist(err) {
if err := DownloadOfacList(); err != nil {
return nil, err
}
}
if err := checker.loadOfacList(); err != nil {
return nil, err
}
return checker, nil
}

func getOfacListPath() string {
currentDirectory, _ := os.Getwd()
for !strings.HasSuffix(currentDirectory, "sdk-go") {
currentDirectory = filepath.Dir(currentDirectory)
}
return filepath.Join(currentDirectory, defaultofacListFilename)
}

func DownloadOfacList() error {
resp, err := http.Get(defaultOfacListURL)
if err != nil {
return err
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
return fmt.Errorf("failed to download OFAC list, status code: %d", resp.StatusCode)
}

outFile, err := os.Create(getOfacListPath())
if err != nil {
return err
}
defer outFile.Close()

_, err = io.Copy(outFile, resp.Body)
if err != nil {
return err
}
return nil
}

func (oc *OfacChecker) loadOfacList() error {
file, err := os.ReadFile(oc.ofacListPath)
if err != nil {
return err
}

err = json.Unmarshal(file, &oc.ofacList)
if err != nil {
return err
}
return nil
}

func (oc *OfacChecker) IsBlacklisted(address string) bool {
for _, item := range oc.ofacList {
if item == address {
return true
}
}
return false
}
2 changes: 1 addition & 1 deletion examples/chain/8_OfflineSigning/example.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ func main() {
}

defaultSubaccountID := chainClient.DefaultSubaccount(senderAddress)
marketId := "0xa508cb32923323679f29a032c70342c147c17d0145625922b0ef22e955c844c0"
marketId := "0x01edfab47f124748dc89998eb33144af734484ba07099014594321729a0ca16b"
amount := decimal.NewFromFloat(2)
price := decimal.NewFromFloat(1.02)

Expand Down
12 changes: 12 additions & 0 deletions examples/chain/ofac/1_DownloadOfacList/example.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package main

import (
chainclient "github.com/InjectiveLabs/sdk-go/client/chain"
)

func main() {
err := chainclient.DownloadOfacList()
if err != nil {
panic(err)
}
}
48 changes: 48 additions & 0 deletions ofac.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
[
"0x179f48c78f57a3a78f0608cc9197b8972921d1d2",
"0x1967d8af5bd86a497fb3dd7899a020e47560daaf",
"0x19aa5fe80d33a56d56c78e82ea5e50e5d80b4dff",
"0x19aa5fe80d33a56d56c78e82ea5e50e5d80b4dff",
"0x1da5821544e25c636c1417ba96ade4cf6d2f9b5a",
"0x2f389ce8bd8ff92de3402ffce4691d17fc4f6535",
"0x2f389ce8bd8ff92de3402ffce4691d17fc4f6535",
"0x2f50508a8a3d323b91336fa3ea6ae50e55f32185",
"0x308ed4b7b49797e1a98d3818bff6fe5385410370",
"0x3cbded43efdaf0fc77b9c55f6fc9988fcc9b757d",
"0x3efa30704d2b8bbac821307230376556cf8cc39e",
"0x48549a34ae37b12f6a30566245176994e17c6b4a",
"0x4f47bc496083c727c5fbe3ce9cdf2b0f6496270c",
"0x4f47bc496083c727c5fbe3ce9cdf2b0f6496270c",
"0x4f47bc496083c727c5fbe3ce9cdf2b0f6496270c",
"0x530a64c0ce595026a4a556b703644228179e2d57",
"0x5512d943ed1f7c8a43f3435c85f7ab68b30121b0",
"0x5a7a51bfb49f190e5a6060a5bc6052ac14a3b59f",
"0x5f48c2a71b2cc96e3f0ccae4e39318ff0dc375b2",
"0x6be0ae71e6c41f2f9d0d1a3b8d0f75e6f6a0b46e",
"0x6f1ca141a28907f78ebaa64fb83a9088b02a8352",
"0x746aebc06d2ae31b71ac51429a19d54e797878e9",
"0x77777feddddffc19ff86db637967013e6c6a116c",
"0x797d7ae72ebddcdea2a346c1834e04d1f8df102b",
"0x8576acc5c05d6ce88f4e49bf65bdf0c62f91353c",
"0x901bb9583b24d97e995513c6778dc6888ab6870e",
"0x961c5be54a2ffc17cf4cb021d863c42dacd47fc1",
"0x97b1043abd9e6fc31681635166d430a458d14f9c",
"0x9c2bc757b66f24d60f016b6237f8cdd414a879fa",
"0x9f4cda013e354b8fc285bf4b9a60460cee7f7ea9",
"0xa7e5d5a720f06526557c513402f2e6b5fa20b008",
"0xb6f5ec1a0a9cd1526536d3f0426c429529471f40",
"0xb6f5ec1a0a9cd1526536d3f0426c429529471f40",
"0xb6f5ec1a0a9cd1526536d3f0426c429529471f40",
"0xc455f7fd3e0e12afd51fba5c106909934d8a0e4a",
"0xca0840578f57fe71599d29375e16783424023357",
"0xd0975b32cea532eadddfc9c60481976e39db3472",
"0xd882cfc20f52f2599d84b8e8d58c7fb62cfe344b",
"0xd882cfc20f52f2599d84b8e8d58c7fb62cfe344b",
"0xe1d865c3d669dcc8c57c8d023140cb204e672ee4",
"0xe7aa314c77f4233c18c6cc84384a9247c0cf367b",
"0xed6e0a7e4ac94d976eebfb82ccf777a3c6bad921",
"0xf3701f445b6bdafedbca97d1e477357839e4120d",
"0xfac583c0cf07ea434052c49115a4682172ab6b4f",
"0xfec8a60023265364d066a1212fde3930f6ae8da7",
"0xffbac21a641dcfe4552920138d90f3638b3c9fba"
]

0 comments on commit a46b970

Please sign in to comment.