diff --git a/x/bridge/observer/bitcoin/bitcoin.go b/x/bridge/observer/bitcoin/bitcoin.go index 73940d4e60a..7a3072890fb 100644 --- a/x/bridge/observer/bitcoin/bitcoin.go +++ b/x/bridge/observer/bitcoin/bitcoin.go @@ -5,7 +5,6 @@ import ( "encoding/hex" "errors" "fmt" - "slices" "strings" "sync/atomic" "time" @@ -205,7 +204,7 @@ func (b *ChainClient) processTx(height uint64, tx *btcjson.TxRawResult) (observe memo, contains := getMemo(tx) if !contains { - return observer.Transfer{}, false, fmt.Errorf("failed to get Tx memo") + return observer.Transfer{}, false, fmt.Errorf("malformed Tx memo") } return observer.Transfer{ @@ -222,7 +221,7 @@ func (b *ChainClient) processTx(height uint64, tx *btcjson.TxRawResult) (observe // isTxRelevant checks if a tx contains transfers to the BTC vault and calculates the total amount to transfer. // This method goes through all the Vouts and accumulates their values if Vouts.account[0] == vault. -// Returns true is the tx is not relevant. Otherwise, false and the total amount to transfer. +// Returns false if the tx is not relevant. Otherwise, true and the total amount to transfer. func (b *ChainClient) isTxRelevant(tx *btcjson.TxRawResult) (math.Uint, bool) { var amount = sdk.NewUint(0) for _, vout := range tx.Vout { @@ -238,7 +237,7 @@ func (b *ChainClient) isTxRelevant(tx *btcjson.TxRawResult) (math.Uint, bool) { continue } - if slices.Contains(addresses, b.vaultAddr) { + if addresses[0] == b.vaultAddr { a, err := getAmount(vout) if err != nil { continue @@ -273,7 +272,10 @@ func getMemo(tx *btcjson.TxRawResult) (string, bool) { // TODO: log? continue } - // TODO: verify the memo format + _, err = sdk.AccAddressFromBech32(string(decoded)) + if err != nil { + continue + } return string(decoded), true } } diff --git a/x/bridge/observer/bitcoin/bitcoin_test.go b/x/bridge/observer/bitcoin/bitcoin_test.go index d8dcd0110e7..94363a9a3ef 100644 --- a/x/bridge/observer/bitcoin/bitcoin_test.go +++ b/x/bridge/observer/bitcoin/bitcoin_test.go @@ -18,6 +18,7 @@ import ( "github.com/cometbft/cometbft/libs/log" "github.com/stretchr/testify/require" + _ "github.com/osmosis-labs/osmosis/v24/app/params" // init Osmosis address prefixes "github.com/osmosis-labs/osmosis/v24/x/bridge/observer" "github.com/osmosis-labs/osmosis/v24/x/bridge/observer/bitcoin" bridgetypes "github.com/osmosis-labs/osmosis/v24/x/bridge/types" @@ -104,9 +105,16 @@ func TestListenOutboundTransfer(t *testing.T) { err = b.Start(ctx) require.NoError(t, err) - // We expect Observer to observe 1 block with 2 Txs - // Only 1 Tx is sent to our vault address, - // so we should receive only 1 TxIn + // We expect Observer to observe 1 block with 8 Txs: + // - tx to our vault but without memo + // - tx to our vault with memo with invalid address + // - tx to our vault but with zero tokens + // - tx to our vault with invalid script type for output Vout + // - tx to our vault with invalid script type for memo Vout + // - tx to our vault with invalid tokens amount + // - unrelated tx + // + valid tx to our vault + // So, we should receive only 1 Transfer txs := b.ListenOutboundTransfer() var out observer.Transfer require.Eventually(t, func() bool { diff --git a/x/bridge/observer/bitcoin/test_responses/block_verbose_tx.json b/x/bridge/observer/bitcoin/test_responses/block_verbose_tx.json index 4e3b027bb5e..ea71d0bdd4c 100644 --- a/x/bridge/observer/bitcoin/test_responses/block_verbose_tx.json +++ b/x/bridge/observer/bitcoin/test_responses/block_verbose_tx.json @@ -10,6 +10,55 @@ "merkleroot": "38250a44321107325d6da5c7af90f4e25a27300d9a8f3c52bc8ece466cc47195", "tx": [ { + "__test_comment": "transaction to the vault 2N4qEFwruq3zznQs78twskBrNTc6kpq87j1 without memo", + "hex": "01000000000101bd0f7fefbee41f76f0678f217b8ef0d23d77037dc9433b757c88ba8b562d848f010000002322002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29ffffffff02102700000000000017a9147f17c07273e76cdd381c2d64b2739c770e9b390887cc3d00000000000017a9140872c5adfb84ba69c97e9298954f61b9706cae4e87040047304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901473044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401475221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae00000000", + "txid": "f395b2cc8551aff25fe8d61fec159a6b93d29b9ff56a68c9d29df99a864fd74c", + "hash": "ef4cd511c64834bde624000b94110c9f184388566a97d68d355339294a72dadf", + "size": 370, + "vsize": 205, + "weight": 820, + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "8f842d568bba887c753b43c97d03773dd2f08e7b218f67f0761fe4beef7f0fbd", + "vout": 1, + "scriptSig": { + "asm": "002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29", + "hex": "22002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29" + }, + "txinwitness": [ + "", + "304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901", + "3044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401", + "5221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.0001, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 7f17c07273e76cdd381c2d64b2739c770e9b3908 OP_EQUAL", + "hex": "a9147f17c07273e76cdd381c2d64b2739c770e9b390887", + "type": "scripthash" + } + }, + { + "value": 0.0001582, + "n": 1, + "scriptPubKey": { + "asm": "OP_HASH160 0872c5adfb84ba69c97e9298954f61b9706cae4e OP_EQUAL", + "hex": "a9140872c5adfb84ba69c97e9298954f61b9706cae4e87", + "type": "scripthash" + } + } + ] + }, + { + "__test_comment": "transaction to the vault 2N4qEFwruq3zznQs78twskBrNTc6kpq87j1 with malformed memo 2N4qEFwruq3zznQs78twskBrNTc6kpq87j1", "hex": "01000000000101bd0f7fefbee41f76f0678f217b8ef0d23d77037dc9433b757c88ba8b562d848f010000002322002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29ffffffff02102700000000000017a9147f17c07273e76cdd381c2d64b2739c770e9b390887cc3d00000000000017a9140872c5adfb84ba69c97e9298954f61b9706cae4e87040047304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901473044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401475221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae00000000", "txid": "f395b2cc8551aff25fe8d61fec159a6b93d29b9ff56a68c9d29df99a864fd74c", "hash": "ef4cd511c64834bde624000b94110c9f184388566a97d68d355339294a72dadf", @@ -54,6 +103,63 @@ "type": "scripthash" } }, + { + "value": 0.0, + "n": 2, + "scriptPubKey": { + "asm": "OP_RETURN 324E3471454677727571337A7A6E517337387477736B42724E5463366B707138376A31", + "hex": "6a23324e3471454677727571337a7a6e517337387477736b42724e5463366b707138376a31", + "type": "nulldata" + } + } + ] + }, + { + "__test_comment": "transaction to the vault 2N4qEFwruq3zznQs78twskBrNTc6kpq87j1 with memo osmo13g23crzfp99xg28nh0j4em4nsqnaur02nek2wt, but with zero tokens amount", + "hex": "01000000000101bd0f7fefbee41f76f0678f217b8ef0d23d77037dc9433b757c88ba8b562d848f010000002322002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29ffffffff02102700000000000017a9147f17c07273e76cdd381c2d64b2739c770e9b390887cc3d00000000000017a9140872c5adfb84ba69c97e9298954f61b9706cae4e87040047304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901473044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401475221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae00000000", + "txid": "f395b2cc8551aff25fe8d61fec159a6b93d29b9ff56a68c9d29df99a864fd74c", + "hash": "ef4cd511c64834bde624000b94110c9f184388566a97d68d355339294a72dadf", + "size": 370, + "vsize": 205, + "weight": 820, + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "8f842d568bba887c753b43c97d03773dd2f08e7b218f67f0761fe4beef7f0fbd", + "vout": 1, + "scriptSig": { + "asm": "002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29", + "hex": "22002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29" + }, + "txinwitness": [ + "", + "304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901", + "3044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401", + "5221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.0, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 7f17c07273e76cdd381c2d64b2739c770e9b3908 OP_EQUAL", + "hex": "a9147f17c07273e76cdd381c2d64b2739c770e9b390887", + "type": "scripthash" + } + }, + { + "value": 0.0, + "n": 1, + "scriptPubKey": { + "asm": "OP_HASH160 0872c5adfb84ba69c97e9298954f61b9706cae4e OP_EQUAL", + "hex": "a9140872c5adfb84ba69c97e9298954f61b9706cae4e87", + "type": "scripthash" + } + }, { "value": 0.0, "n": 2, @@ -66,6 +172,178 @@ ] }, { + "__test_comment": "invalid Vout script type for output tx", + "hex": "01000000000101bd0f7fefbee41f76f0678f217b8ef0d23d77037dc9433b757c88ba8b562d848f010000002322002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29ffffffff02102700000000000017a9147f17c07273e76cdd381c2d64b2739c770e9b390887cc3d00000000000017a9140872c5adfb84ba69c97e9298954f61b9706cae4e87040047304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901473044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401475221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae00000000", + "txid": "f395b2cc8551aff25fe8d61fec159a6b93d29b9ff56a68c9d29df99a864fd74c", + "hash": "ef4cd511c64834bde624000b94110c9f184388566a97d68d355339294a72dadf", + "size": 370, + "vsize": 205, + "weight": 820, + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "8f842d568bba887c753b43c97d03773dd2f08e7b218f67f0761fe4beef7f0fbd", + "vout": 1, + "scriptSig": { + "asm": "002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29", + "hex": "22002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29" + }, + "txinwitness": [ + "", + "304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901", + "3044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401", + "5221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.0001, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 7f17c07273e76cdd381c2d64b2739c770e9b3908 OP_EQUAL", + "hex": "a9147f17c07273e76cdd381c2d64b2739c770e9b390887", + "type": "nulldata" + } + }, + { + "value": 0.0001582, + "n": 1, + "scriptPubKey": { + "asm": "OP_HASH160 0872c5adfb84ba69c97e9298954f61b9706cae4e OP_EQUAL", + "hex": "a9140872c5adfb84ba69c97e9298954f61b9706cae4e87", + "type": "scripthash" + } + }, + { + "value": 0.0, + "n": 2, + "scriptPubKey": { + "asm": "OP_RETURN 6f736d6f313367323363727a66703939786732386e68306a34656d346e73716e61757230326e656b327774", + "hex": "6a2b6f736d6f313367323363727a66703939786732386e68306a34656d346e73716e61757230326e656b327774", + "type": "nulldata" + } + } + ] + }, + { + "__test_comment": "invalid Vout script type for memo", + "hex": "01000000000101bd0f7fefbee41f76f0678f217b8ef0d23d77037dc9433b757c88ba8b562d848f010000002322002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29ffffffff02102700000000000017a9147f17c07273e76cdd381c2d64b2739c770e9b390887cc3d00000000000017a9140872c5adfb84ba69c97e9298954f61b9706cae4e87040047304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901473044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401475221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae00000000", + "txid": "f395b2cc8551aff25fe8d61fec159a6b93d29b9ff56a68c9d29df99a864fd74c", + "hash": "ef4cd511c64834bde624000b94110c9f184388566a97d68d355339294a72dadf", + "size": 370, + "vsize": 205, + "weight": 820, + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "8f842d568bba887c753b43c97d03773dd2f08e7b218f67f0761fe4beef7f0fbd", + "vout": 1, + "scriptSig": { + "asm": "002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29", + "hex": "22002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29" + }, + "txinwitness": [ + "", + "304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901", + "3044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401", + "5221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.0001, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 7f17c07273e76cdd381c2d64b2739c770e9b3908 OP_EQUAL", + "hex": "a9147f17c07273e76cdd381c2d64b2739c770e9b390887", + "type": "scripthash" + } + }, + { + "value": 0.0001582, + "n": 1, + "scriptPubKey": { + "asm": "OP_HASH160 0872c5adfb84ba69c97e9298954f61b9706cae4e OP_EQUAL", + "hex": "a9140872c5adfb84ba69c97e9298954f61b9706cae4e87", + "type": "scripthash" + } + }, + { + "value": 0.0, + "n": 2, + "scriptPubKey": { + "asm": "OP_RETURN 6f736d6f313367323363727a66703939786732386e68306a34656d346e73716e61757230326e656b327774", + "hex": "6a2b6f736d6f313367323363727a66703939786732386e68306a34656d346e73716e61757230326e656b327774", + "type": "scripthash" + } + } + ] + }, + { + "__test_comment": "invalid output value amount", + "hex": "01000000000101bd0f7fefbee41f76f0678f217b8ef0d23d77037dc9433b757c88ba8b562d848f010000002322002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29ffffffff02102700000000000017a9147f17c07273e76cdd381c2d64b2739c770e9b390887cc3d00000000000017a9140872c5adfb84ba69c97e9298954f61b9706cae4e87040047304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901473044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401475221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae00000000", + "txid": "f395b2cc8551aff25fe8d61fec159a6b93d29b9ff56a68c9d29df99a864fd74c", + "hash": "ef4cd511c64834bde624000b94110c9f184388566a97d68d355339294a72dadf", + "size": 370, + "vsize": 205, + "weight": 820, + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "8f842d568bba887c753b43c97d03773dd2f08e7b218f67f0761fe4beef7f0fbd", + "vout": 1, + "scriptSig": { + "asm": "002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29", + "hex": "22002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29" + }, + "txinwitness": [ + "", + "304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901", + "3044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401", + "5221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": null, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 7f17c07273e76cdd381c2d64b2739c770e9b3908 OP_EQUAL", + "hex": "a9147f17c07273e76cdd381c2d64b2739c770e9b390887", + "type": "scripthash" + } + }, + { + "value": 0.0001582, + "n": 1, + "scriptPubKey": { + "asm": "OP_HASH160 0872c5adfb84ba69c97e9298954f61b9706cae4e OP_EQUAL", + "hex": "a9140872c5adfb84ba69c97e9298954f61b9706cae4e87", + "type": "scripthash" + } + }, + { + "value": 0.0, + "n": 2, + "scriptPubKey": { + "asm": "OP_RETURN 6f736d6f313367323363727a66703939786732386e68306a34656d346e73716e61757230326e656b327774", + "hex": "6a2b6f736d6f313367323363727a66703939786732386e68306a34656d346e73716e61757230326e656b327774", + "type": "nulldata" + } + } + ] + }, + { + "__test_comment": "unrelated transaction", "hex": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff2303736827044d82f9654d65726d61696465722046545721010001e167ee2e0000000000ffffffff032202000000000000225120acf3ff3ad88d47dc0ac7c564eb53092b6bee2947a8cd329c7090e157442a85f29bd31c0000000000160014c035e789d9efffa10aa92e93f48f29b8cfb224c20000000000000000266a24aa21a9ede0e2d47bfe8573127a173413e5fde153a26c2bcf89819d8982bf3601c7c6e63a0120000000000000000000000000000000000000000000000000000000000000000000000000", "txid": "31cc99caabaac37e433ba75ad82d67f4dd9f161a940f38d99c9da95202822814", "hash": "bb928d1961dab347d52194bc9e2988348cca16b3daffc9d835a241b73fa05be4", @@ -108,7 +386,7 @@ } }, { - "value": 0, + "value": 0.0, "n": 2, "scriptPubKey": { "asm": "OP_RETURN aa21a9ede0e2d47bfe8573127a173413e5fde153a26c2bcf89819d8982bf3601c7c6e63a", @@ -121,6 +399,63 @@ "confirmations": 1, "time": 1710850637, "blocktime": 1710850637 + }, + { + "__test_comment": "valid transaction to the vault 2N4qEFwruq3zznQs78twskBrNTc6kpq87j1 with memo osmo13g23crzfp99xg28nh0j4em4nsqnaur02nek2wt", + "hex": "01000000000101bd0f7fefbee41f76f0678f217b8ef0d23d77037dc9433b757c88ba8b562d848f010000002322002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29ffffffff02102700000000000017a9147f17c07273e76cdd381c2d64b2739c770e9b390887cc3d00000000000017a9140872c5adfb84ba69c97e9298954f61b9706cae4e87040047304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901473044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401475221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae00000000", + "txid": "f395b2cc8551aff25fe8d61fec159a6b93d29b9ff56a68c9d29df99a864fd74c", + "hash": "ef4cd511c64834bde624000b94110c9f184388566a97d68d355339294a72dadf", + "size": 370, + "vsize": 205, + "weight": 820, + "version": 1, + "locktime": 0, + "vin": [ + { + "txid": "8f842d568bba887c753b43c97d03773dd2f08e7b218f67f0761fe4beef7f0fbd", + "vout": 1, + "scriptSig": { + "asm": "002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29", + "hex": "22002046fbc84ccf1163f3556e59cd98778897f00fc898816106efd096b68fa4361b29" + }, + "txinwitness": [ + "", + "304402202a02c91ad3e046b924166f451a6fdf7f72fdaca63d338ff9aba3465537553bff0220478e20adc238999354275aa6665bcdd09538834d42eadcbd24d10ea763b4176901", + "3044022059fcb64ef800d07d3ec383fcfad5331e1335a49e786094426049ea4f0fe99694022046669e4fe71dff08928e031bf801718be26b515b1fafc808a220d01dad3f2d4401", + "5221027299b68fa5a810b23dffdc44a9c618da489f55da55d985d39fd06205f74e03872103cad331ece1c0d820bab125237e04536cb5021f1a9bf8a5ae36a82529c98c629952ae" + ], + "sequence": 4294967295 + } + ], + "vout": [ + { + "value": 0.0001, + "n": 0, + "scriptPubKey": { + "asm": "OP_HASH160 7f17c07273e76cdd381c2d64b2739c770e9b3908 OP_EQUAL", + "hex": "a9147f17c07273e76cdd381c2d64b2739c770e9b390887", + "type": "scripthash" + } + }, + { + "value": 0.0001582, + "n": 1, + "scriptPubKey": { + "asm": "OP_HASH160 0872c5adfb84ba69c97e9298954f61b9706cae4e OP_EQUAL", + "hex": "a9140872c5adfb84ba69c97e9298954f61b9706cae4e87", + "type": "scripthash" + } + }, + { + "value": 0.0, + "n": 2, + "scriptPubKey": { + "asm": "OP_RETURN 6f736d6f313367323363727a66703939786732386e68306a34656d346e73716e61757230326e656b327774", + "hex": "6a2b6f736d6f313367323363727a66703939786732386e68306a34656d346e73716e61757230326e656b327774", + "type": "nulldata" + } + } + ] } ], "time": 1710863896, diff --git a/x/bridge/observer/osmosis/osmosis.go b/x/bridge/observer/osmosis/osmosis.go index fcdee009799..7b6d2e44e46 100644 --- a/x/bridge/observer/osmosis/osmosis.go +++ b/x/bridge/observer/osmosis/osmosis.go @@ -8,6 +8,9 @@ import ( errorsmod "cosmossdk.io/errors" "cosmossdk.io/math" + "github.com/btcsuite/btcd/btcutil" + btcdchaincfg "github.com/btcsuite/btcd/chaincfg" + abcitypes "github.com/cometbft/cometbft/abci/types" "github.com/cometbft/cometbft/libs/log" rpchttp "github.com/cometbft/cometbft/rpc/client/http" coretypes "github.com/cometbft/cometbft/rpc/core/types" @@ -19,6 +22,13 @@ import ( bridgetypes "github.com/osmosis-labs/osmosis/v24/x/bridge/types" ) +type Mode = string + +const ( + ModeMainnet Mode = "mainnet" + ModeTestnet Mode = "testnet" +) + var ( ModuleName = "osmosis-chain" OsmoGasLimit = uint64(200000) @@ -28,6 +38,7 @@ var ( type ChainClient struct { logger log.Logger + mode Mode osmoClient *Client cometRpc *rpchttp.HTTP stopChan chan struct{} @@ -40,6 +51,7 @@ type ChainClient struct { // NewChainClient returns new instance of `Osmosis` func NewChainClient( logger log.Logger, + mode Mode, osmoClient *Client, cometRpc *rpchttp.HTTP, txConfig cosmosclient.TxConfig, @@ -47,6 +59,7 @@ func NewChainClient( ) *ChainClient { return &ChainClient{ logger: logger.With("module", ModuleName), + mode: mode, osmoClient: osmoClient, cometRpc: cometRpc, stopChan: make(chan struct{}), @@ -114,7 +127,7 @@ func (c *ChainClient) SignalInboundTransfer(ctx context.Context, in observer.Tra if err != nil { return errorsmod.Wrapf(err, "Failed to sign tx for inbound transfer %s", in.Id) } - _, err = c.osmoClient.BroadcastTx(ctx, bytes) + resp, err := c.osmoClient.BroadcastTx(ctx, bytes) if err != nil { return errorsmod.Wrapf( err, @@ -122,6 +135,9 @@ func (c *ChainClient) SignalInboundTransfer(ctx context.Context, in observer.Tra in.Id, ) } + if resp.Code != abcitypes.CodeTypeOK { + return fmt.Errorf("Tx for inbound transfer %s failed inside Osmosis: %s", in.Id, resp.RawLog) + } return nil } @@ -170,6 +186,7 @@ func (c *ChainClient) processNewBlockTxs(ctx context.Context, height uint64, txs )) continue } + if res.IsErr() { continue } @@ -180,6 +197,21 @@ func (c *ChainClient) processNewBlockTxs(ctx context.Context, height uint64, txs continue } + err = verifyOutboundDestAddress( + c.mode, + observer.ChainId(outbound.AssetId.SourceChain), + outbound.DestAddr, + ) + if err != nil { + c.logger.Error(fmt.Sprintf( + "Invalid outbound destination address in Tx %s, block %d: %s", + txHash, + height, + err.Error(), + )) + continue + } + out := outboundTransferFromMsg( height, txHash, @@ -218,6 +250,21 @@ func outboundTransferFromMsg( } } +func verifyOutboundDestAddress(mode Mode, chainId observer.ChainId, addr string) error { + switch chainId { + case observer.ChainIdBitcoin: + switch mode { + case ModeMainnet: + _, err := btcutil.DecodeAddress(addr, &btcdchaincfg.MainNetParams) + return err + case ModeTestnet: + _, err := btcutil.DecodeAddress(addr, &btcdchaincfg.TestNet3Params) + return err + } + } + return fmt.Errorf("Unsupported outbound destination chain: %s", chainId) +} + // Height returns current height of the chain func (c *ChainClient) Height(context.Context) (uint64, error) { return c.lastObservedHeight.Load(), nil diff --git a/x/bridge/observer/osmosis/osmosis_test.go b/x/bridge/observer/osmosis/osmosis_test.go index 619bbfa031f..0dba3721073 100644 --- a/x/bridge/observer/osmosis/osmosis_test.go +++ b/x/bridge/observer/osmosis/osmosis_test.go @@ -3,6 +3,7 @@ package osmosis_test import ( "context" "encoding/json" + "io" "net/http" "net/http/httptest" "os" @@ -100,6 +101,7 @@ func NewOsmosisTestSuite(t *testing.T, ctx context.Context) OsmosisTestSuite { client := osmosis.NewClient(ChainId, conn, kr, app.GetEncodingConfig().TxConfig) o := osmosis.NewChainClient( log.NewNopLogger(), + osmosis.ModeTestnet, client, cometRpc, app.GetEncodingConfig().TxConfig, @@ -134,13 +136,19 @@ func readNewBlockEvent(t *testing.T, path string) coretypes.ResultEvent { return result } -func readTxCheck(t *testing.T, path string) abci.ResponseCheckTx { +func readTxCheckBytes(t *testing.T, id int, path string) []byte { dataStr, err := os.ReadFile(path) require.NoError(t, err) result := abci.ResponseCheckTx{} err = json.Unmarshal([]byte(dataStr), &result) require.NoError(t, err) - return result + checkResultsResp := cmtrpctypes.NewRPCSuccessResponse( + cmtrpctypes.JSONRPCIntID(id), + result, + ) + checkResultsRaw, err := json.Marshal(checkResultsResp) + require.NoError(t, err) + return checkResultsRaw } func success(t *testing.T) http.HandlerFunc { @@ -150,7 +158,7 @@ func success(t *testing.T) http.HandlerFunc { c, err := upgrader.Upgrade(w, r, nil) require.NoError(t, err) defer c.Close() - newBlock := readNewBlockEvent(t, "./test_events/new_block_success.json") + newBlock := readNewBlockEvent(t, "./test_events/new_block.json") newBlockResp := cmtrpctypes.NewRPCSuccessResponse( cmtrpctypes.JSONRPCIntID(1), newBlock, @@ -160,14 +168,25 @@ func success(t *testing.T) http.HandlerFunc { err = c.WriteMessage(1, newBlockRaw) require.NoError(t, err) case http.MethodPost: - checkResults := readTxCheck(t, "./test_events/tx_check_success.json") - checkResultsResp := cmtrpctypes.NewRPCSuccessResponse( - cmtrpctypes.JSONRPCIntID(0), - checkResults, - ) - checkResultsRaw, err := json.Marshal(checkResultsResp) + bytes, err := io.ReadAll(r.Body) + require.NoError(t, err) + require.NoError(t, r.Body.Close()) + var req cmtrpctypes.RPCRequest + err = json.Unmarshal(bytes, &req) require.NoError(t, err) - _, err = w.Write(checkResultsRaw) + jsonId, ok := req.ID.(cmtrpctypes.JSONRPCIntID) + require.True(t, ok) + id := int(jsonId) + + var resp []byte + switch id { + case 1: + resp = readTxCheckBytes(t, id, "./test_events/tx_check_error.json") + default: + resp = readTxCheckBytes(t, id, "./test_events/tx_check_success.json") + } + + _, err = w.Write(resp) require.NoError(t, err) default: t.Fatal("Unexpected request method", r.Method) @@ -282,6 +301,11 @@ func TestListenOutboundTransfer(t *testing.T) { ots.Start(t, ctx) defer ots.Stop(t, ctx) + // We expect to observe 1 block with 3 Txs each with a `MsgOutboundTransfer` message: + // - valid tx to BTC address + // - failed tx + // - tx with invalid destination address + // So, we should to receive only 1 Transfer transfers := ots.o.ListenOutboundTransfer() var transfer observer.Transfer require.Eventually(t, func() bool { @@ -292,8 +316,8 @@ func TestListenOutboundTransfer(t *testing.T) { expTransfer := observer.Transfer{ SrcChain: observer.ChainIdOsmosis, DstChain: observer.ChainIdBitcoin, - Id: "8593aa191651f6a3e2978fb5334b3e5b1e20abd72ad539f15c76f241fa696d3e", - Height: 881, + Id: "8eb4b69be7144690f82a4e1485f4b85d23adc5267db5d3dab7affae57c8ce2a4", + Height: 2801, Sender: "osmo1pldlhnwegsj3lqkarz0e4flcsay3fuqgkd35ww", To: "2Mt1ttL5yffdfCGxpfxmceNE4CRUcAsBbgQ", Asset: bridgetypes.DefaultBitcoinDenomName, diff --git a/x/bridge/observer/osmosis/test_events/new_block_success.json b/x/bridge/observer/osmosis/test_events/new_block.json similarity index 58% rename from x/bridge/observer/osmosis/test_events/new_block_success.json rename to x/bridge/observer/osmosis/test_events/new_block.json index 8b8db8cebc3..6c7c5ea9939 100644 --- a/x/bridge/observer/osmosis/test_events/new_block_success.json +++ b/x/bridge/observer/osmosis/test_events/new_block.json @@ -9,49 +9,51 @@ "block": "11" }, "chain_id": "my-test-chain", - "height": "881", - "time": "2024-04-01T14:17:10.715933958Z", + "height": "2801", + "time": "2024-04-03T09:47:40.739349321Z", "last_block_id": { - "hash": "55E2A190F9F1877A1C390F557E3208E45F2C5BD2F782C76E03ACB4266B17A359", + "hash": "82A6EA06C84B7BF17D3173E1232B5CE820DFC7D7417B2E141B0CDB8F855F3CA1", "parts": { "total": 1, - "hash": "D8E296FACC516BD1010A1BB4C9ABCCC512E2821873956839AA17C10FA63A911E" + "hash": "0D5C54C6C15823FFE26B70B62BDCC0C5402C41804FD3AE1F3A1728E84AEEDC5C" } }, - "last_commit_hash": "428E15CBA9DB1089C9FC9BACE274CEA0F2664115F365D4AC3069425BDAEE4035", - "data_hash": "CF137839AB61FD2046F0553A2F986504D2F37497120456D03391A9FF21992993", + "last_commit_hash": "1F5E3CFD4001F02DFC16457F8E4B297EF43FF3AA9F989BD8F95F0F7E13990355", + "data_hash": "70CF44DFF1E91F23C1474508793250E0E97BB5647D6E495A7378438E67389DB7", "validators_hash": "1E811B4CEEF78E840C253AB5928C670A6A6B389A231496E9ABF8084FD6206FAF", "next_validators_hash": "1E811B4CEEF78E840C253AB5928C670A6A6B389A231496E9ABF8084FD6206FAF", "consensus_hash": "048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F", - "app_hash": "281B4F7AFCF115D4F72A47A1BDC03A59F180C32E9719FED4D48C3A6FCC2C7DAF", + "app_hash": "7573ABD8364FF3B5F118F7A64FF1BE39268CA710A72952021CE7CB1C267F4B89", "last_results_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", "evidence_hash": "E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855", "proposer_address": "B81ECB7226036A4B8ACFC81EBDF24A2527D25BE8" }, "data": { "txs": [ - "CpgBCpUBCisvb3Ntb3Npcy5icmlkZ2UudjFiZXRhMS5Nc2dPdXRib3VuZFRyYW5zZmVyEmYKK29zbW8xcGxkbGhud2Vnc2ozbHFrYXJ6MGU0Zmxjc2F5M2Z1cWdrZDM1d3cSIzJNdDF0dEw1eWZmZGZDR3hwZnhtY2VORTRDUlVjQXNCYmdRGg4KB2JpdGNvaW4SA2J0YyICMTASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAqGxGGMLwZu1ElZA/wZ9hdIPpYUzRWaKUtzzwx6sRbZlEgQKAggBGAgSEwoNCgVzdGFrZRIEMTAwMBDAmgwaQGATrlA3NQt5cMVeUjt1i0YngPv2SP3cdob0wJlZekreByBudAJnxJpWhLCuWKkrAZVCaXtHrV4SoYUsvHfKhMg=" + "CqABCp0BCisvb3Ntb3Npcy5icmlkZ2UudjFiZXRhMS5Nc2dPdXRib3VuZFRyYW5zZmVyEm4KK29zbW8xcGxkbGhud2Vnc2ozbHFrYXJ6MGU0Zmxjc2F5M2Z1cWdrZDM1d3cSK29zbW8xbWZrbDRwOTJscXZsZjdmaDVycGRzM2FmdnBhc2U3OGZ6NWgzNmwaDgoHYml0Y29pbhIDYnRjIgIxMBJnClAKRgofL2Nvc21vcy5jcnlwdG8uc2VjcDI1NmsxLlB1YktleRIjCiECobEYYwvBm7USVkD/Bn2F0g+lhTNFZopS3PPDHqxFtmUSBAoCCAEYDRITCg0KBXN0YWtlEgQxMDAwEMCaDBpAeyBpiYpl0iBgBB2g5OZyvKSgYT9pOrW4uxtsVV/IZ1pvbA1hcjXKNk9IKyL8Gcv6eS05ZXSbBmwtBY3L7Dey1w==", + "CpgBCpUBCisvb3Ntb3Npcy5icmlkZ2UudjFiZXRhMS5Nc2dPdXRib3VuZFRyYW5zZmVyEmYKK29zbW8xcGxkbGhud2Vnc2ozbHFrYXJ6MGU0Zmxjc2F5M2Z1cWdrZDM1d3cSIzJNdDF0dEw1eWZmZGZDR3hwZnhtY2VORTRDUlVjQXNCYmdRGg4KB2JpdGNvaW4SA2J0YyICMTASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAqGxGGMLwZu1ElZA/wZ9hdIPpYUzRWaKUtzzwx6sRbZlEgQKAggBGA4SEwoNCgVzdGFrZRIEMTAwMBDAmgwaQIvlf1BifOQRKamSTBaUK05SUMzybrfYfNYH3vvEBpuFW/w2H4p7YIPYKnhsdYv7D1pZCpFHfNZ5rScRIjnFlwg=", + "CpgBCpUBCisvb3Ntb3Npcy5icmlkZ2UudjFiZXRhMS5Nc2dPdXRib3VuZFRyYW5zZmVyEmYKK29zbW8xcGxkbGhud2Vnc2ozbHFrYXJ6MGU0Zmxjc2F5M2Z1cWdrZDM1d3cSIzJNdDF0dEw1eWZmZGZDR3hwZnhtY2VORTRDUlVjQXNCYmdRGg4KB2JpdGNvaW4SA2J0YyICMTASZwpQCkYKHy9jb3Ntb3MuY3J5cHRvLnNlY3AyNTZrMS5QdWJLZXkSIwohAqGxGGMLwZu1ElZA/wZ9hdIPpYUzRWaKUtzzwx6sRbZlEgQKAggBGA8SEwoNCgVzdGFrZRIEMTAwMBDAmgwaQLlOXD9HIHgLrYbxtCyViEoaqlosMgqqGhHux4fQHPZ0E/rfxPNhq4lpJ9LzW4u2WKsmNsEMLYfMtIwd4IgScy4=" ] }, "evidence": { "evidence": null }, "last_commit": { - "height": "880", + "height": "2800", "round": 0, "block_id": { - "hash": "55E2A190F9F1877A1C390F557E3208E45F2C5BD2F782C76E03ACB4266B17A359", + "hash": "82A6EA06C84B7BF17D3173E1232B5CE820DFC7D7417B2E141B0CDB8F855F3CA1", "parts": { "total": 1, - "hash": "D8E296FACC516BD1010A1BB4C9ABCCC512E2821873956839AA17C10FA63A911E" + "hash": "0D5C54C6C15823FFE26B70B62BDCC0C5402C41804FD3AE1F3A1728E84AEEDC5C" } }, "signatures": [ { "block_id_flag": 2, "validator_address": "B81ECB7226036A4B8ACFC81EBDF24A2527D25BE8", - "timestamp": "2024-04-01T14:17:10.715933958Z", - "signature": "yv+UN4V8RfB7JceRW2QrWLOlIyZwlp5421aAC9UH6BL3OBIIKHDGhy5nPFDQ19BQIreejLC1LSkbwhDSjMhGBw==" + "timestamp": "2024-04-03T09:47:40.739349321Z", + "signature": "qyR90k0Xw6WnZ8lORWtzcS3JJcFoA5tc6ahG32TXle/frx6xUgZ3gHRxwA/Ofy2CEnB5Y0MGkDYg9Jfy18ZQAw==" } ] } diff --git a/x/bridge/observer/osmosis/test_events/tx_check_error.json b/x/bridge/observer/osmosis/test_events/tx_check_error.json new file mode 100644 index 00000000000..36021b9d65b --- /dev/null +++ b/x/bridge/observer/osmosis/test_events/tx_check_error.json @@ -0,0 +1,13 @@ +{ + "code": 32, + "data": null, + "log": "account sequence mismatch, expected 10, got 9: incorrect account sequence", + "info": "", + "gas_wanted": "200000", + "gas_used": "39457", + "events": [], + "codespace": "sdk", + "sender": "", + "priority": "0", + "mempoolError": "" +} \ No newline at end of file