Files
simplex-chat/tests/Bots/StoreService/NameRegistrationTests.hs
T

168 lines
11 KiB
Haskell

{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Bots.StoreService.NameRegistrationTests (badgeNameRegistrationTests) where
import StoreService.Names.Chain
import StoreService.Names.Registration
import StoreService.Names.Revert (RevertName (..))
import Bots.StoreService.FakeEthNode
import Bots.StoreService.WebUIMock
import Control.Concurrent.STM (TVar)
import Crypto.Random (ChaChaDRG)
import Data.ByteArray.Encoding (Base (Base16), convertFromBase, convertToBase)
import Data.ByteString (ByteString)
import qualified Data.ByteString as B
import Data.Text (Text)
import Data.Word (Word64)
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Encoding.String (strDecode)
import Simplex.Messaging.Eth.Address (Address)
import Simplex.Messaging.Eth.EIP712 (Eip712Domain)
import Simplex.Messaging.Eth.Transaction (Eip1559Tx (..), signEip1559Tx)
import Simplex.Messaging.SimplexName (SimplexTLD (..))
import Test.Hspec
badgeNameRegistrationTests :: Spec
badgeNameRegistrationTests = describe "StoreService names: registration" $ do
g <- runIO C.newRandom
-- expected encodings and signatures are viem's, against the SimplexController, SimplexSponsor and SimplexResolver ABIs
describe "encoding" $ do
it "encodes registerWithCredit with records" $ testRegisterCalldataWithRecords g
it "encodes registerWithCredit without records" $ testRegisterCalldataWithoutRecords g
it "encodes commit with the commitment of a registration" testCommitCalldata
describe "verifying the register intent" $ do
it "rejects an intent signed by another wallet" $ testVerifyOtherSigner g
it "rejects an intent past its deadline" $ testVerifyExpired g
it "rejects a secret that is not 32 bytes" $ testVerifyShortSecret g
it "reads the register's dry run reverting as the sponsor's error" $ testRevertedDryRun g
it "registers a name the client committed to without revealing it" $ testCommitThenRegister g
testRegisterCalldataWithRecords :: TVar ChaChaDRG -> IO ()
testRegisterCalldataWithRecords g = do
intent <- knownIntent g knownRegistration
toHex (registerWithCreditCalldata intent)
`shouldBe` "c4bd2ab50000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f4865700000000000000000000000000000000000000000000000000000000000000046000000000000000000000000000000000000000000000000000000000000001000000000000000000000000009858effd232b4033e47d90003d41ec34ecaeda940000000000000000000000000000000000000000000000000000000003c2670011111111111111111111111111111111111111111111111111111111111111110000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe6000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000762616b657279310000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000000000000000000000000000016000000000000000000000000000000000000000000000000000000000000000e410f13a8c58c186cadf476923d25ea4950487d1aaf21906ba5574c87f5b9435b61b994370000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000f73696d706c65782e636f6e746163740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002068747470733a2f2f736d702e73696d706c65782e696d2f6123636f6e746163740000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e410f13a8c58c186cadf476923d25ea4950487d1aaf21906ba5574c87f5b9435b61b994370000000000000000000000000000000000000000000000000000000000000006000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000f73696d706c65782e6368616e6e656c0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001d68747470733a2f2f736d70312e73696d706c65782e696d2f63236f6e65000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000041f310b273d0e0ab0975a66d383fe818a5ec3474fcd922dbf3fe70094a3c0ceae86559b13ea0728a330613c6fe236a89a026daf345dcc8c0616e9eda2c69cbeb091b00000000000000000000000000000000000000000000000000000000000000"
testRegisterCalldataWithoutRecords :: TVar ChaChaDRG -> IO ()
testRegisterCalldataWithoutRecords g = do
intent <- knownIntent g knownRegistration {rContactLink = Nothing, rChannelLink = Nothing}
toHex (registerWithCreditCalldata intent)
`shouldBe` "c4bd2ab50000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000f486570000000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000001000000000000000000000000009858effd232b4033e47d90003d41ec34ecaeda940000000000000000000000000000000000000000000000000000000003c2670011111111111111111111111111111111111111111111111111111111111111110000000000000000000000002279b7a0a67db372996a5fab50d91eaa73d2ebe6000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000762616b6572793100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000411d0b3a477c99fbcadada2937b71127bf6da202f5cd25856649829653d8cf6a8c0ce0afb8505e398e23b3b01fcb3b17ffcf82d8ba6d24e9b854506894b2864ecf1c00000000000000000000000000000000000000000000000000000000000000"
testCommitCalldata :: IO ()
testCommitCalldata =
toHex (commitCalldata $ makeCommitment knownRegistration) `shouldBe` "f14fcbc8810f0645b8d9839e3744bb956f9da4fa12bb51ec97459e08739c576a36cba0df"
testVerifyOtherSigner :: TVar ChaChaDRG -> IO ()
testVerifyOtherSigner g = do
other <- newEphemeralWallet g
intent <- signRegisterIntent g domain other knownRegistration deadline
verifyRegisterIntent domain deadline intent `shouldReturn` Left "intent is not signed by the owner"
testVerifyExpired :: TVar ChaChaDRG -> IO ()
testVerifyExpired g = do
intent <- knownIntent g knownRegistration
verifyRegisterIntent domain (deadline + 1) intent `shouldReturn` Left "intent expired"
testVerifyShortSecret :: TVar ChaChaDRG -> IO ()
testVerifyShortSecret g = do
intent <- knownIntent g knownRegistration {rSecret = B.replicate 31 0x11}
verifyRegisterIntent domain deadline intent `shouldReturn` Left "secret: expected 32 bytes"
testRevertedDryRun :: TVar ChaChaDRG -> IO ()
testRevertedDryRun g = withFakeEthNode $ \node -> do
-- viem's encodeErrorResult of InsufficientAllowance(10000, 2500)
setFault node "eth_estimateGas" $ revertFault $ fromHex "2a1b2dd8000000000000000000000000000000000000000000000000000000000000271000000000000000000000000000000000000000000000000000000000000009c4"
intent <- knownIntent g knownRegistration
c <- newChainClient g $ fakeChainConfig node
estimateGas c fakeSponsor (registerWithCreditCalldata intent) `shouldReturn` Left (ChainReverted $ Just RNInsufficientAllowance)
testCommitThenRegister :: TVar ChaChaDRG -> IO ()
testCommitThenRegister g = withFakeEthNode $ \node -> do
c <- newChainClient g $ fakeChainConfig node
w <- newEphemeralWallet g
ewAddress <$> walletFromCode g (redemptionCode w) `shouldReturn` ewAddress w
let registration = knownRegistration {rOwner = ewAddress w}
commitCall = commitCalldata $ makeCommitment registration
commitTx <- expectedTransaction g 0 fakeController commitCall
send g c fakeController commitCall `shouldReturn` SignedTx commitTx (TxHash $ C.keccak256 commitTx)
intent <- signRegisterIntent g domain w registration deadline
verifyRegisterIntent domain deadline intent `shouldReturn` Right ()
let registerCall = registerWithCreditCalldata intent
registerTx <- expectedTransaction g 1 fakeSponsor registerCall
send g c fakeSponsor registerCall `shouldReturn` SignedTx registerTx (TxHash $ C.keccak256 registerTx)
sentTransactions node `shouldReturn` [commitTx, registerTx]
-- | The dry run, the registrar's next nonce and the fees, then the signed transaction sent and its hash checked.
send :: TVar ChaChaDRG -> ChainClient -> Address -> ByteString -> IO SignedTx
send g c@ChainClient {ccRegistrar} to calldata = do
gas <- right' =<< estimateGas c to calldata
nonce <- right' =<< transactionCount c ccRegistrar Pending
tip <- right' =<< maxPriorityFee c
Block {blockBaseFee} <- right' =<< latestBlock c
tx@SignedTx {stRaw, stHash} <-
right <$> signTransaction g c TxRequest {trTo = to, trData = calldata, trNonce = nonce, trGasLimit = gas, trMaxFeePerGas = 2 * blockBaseFee + tip, trMaxPriorityFeePerGas = tip}
sendRawTransaction c stRaw `shouldReturn` Right stHash
pure tx
where
right' = either (fail . show) pure
-- fixtures
knownRegistration :: Registration
knownRegistration =
Registration
{ rLabel = "bakery1",
rTLD = TLDSimplex,
rOwner = address "0x9858EfFD232B4033E47d90003D41EC34EcaEda94",
rDuration = 63072000,
rSecret = B.replicate 32 0x11,
rResolver = address "0x2279b7a0a67db372996a5fab50d91eaa73d2ebe6",
rContactLink = Just "https://smp.simplex.im/a#contact",
rChannelLink = Just "https://smp1.simplex.im/c#one"
}
knownIntent :: TVar ChaChaDRG -> Registration -> IO RegisterIntent
knownIntent g r = do
w <- walletFromCode g knownCode
signRegisterIntent g domain w r deadline
knownCode :: Text
knownCode = "SN-00000-00000-00000-00000-00000-0VME2"
domain :: Eip712Domain
domain = sponsorDomain fakeChainId fakeSponsor
deadline :: Word64
deadline = 4102444800
expectedTransaction :: TVar ChaChaDRG -> Word64 -> Address -> ByteString -> IO ByteString
expectedTransaction g nonce to calldata =
right
<$> signEip1559Tx
g
fakeRegistrarKey
Eip1559Tx
{ txChainId = fakeChainId,
txNonce = nonce,
txMaxPriorityFeePerGas = fakeTip,
txMaxFeePerGas = 2 * fakeBaseFee + fakeTip,
txGasLimit = fakeGas,
txTo = to,
txValue = 0,
txData = calldata
}
address :: ByteString -> Address
address = right . strDecode
toHex :: ByteString -> ByteString
toHex = convertToBase Base16
fromHex :: ByteString -> ByteString
fromHex = right . convertFromBase Base16
right :: Either String a -> a
right = either (error . ("unexpected Left: " <>)) id