mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-11-04 08:37:30 +00:00
round 11 converged
This commit is contained in:
1 parent
f85d53e918
commit
9c524dd28a
9 files changed
+64
-47
No files matched your search
@@ -14,7 +14,7 @@ where
|
||||
import BadgeService.Names.Registration (RegisterIntent, commitCalldata, registerWithCreditCalldata)
|
||||
import Control.Concurrent.STM (TVar)
|
||||
import Control.Exception (try)
|
||||
import Control.Monad.Except (ExceptT, liftEither, runExceptT)
|
||||
import Control.Monad.Except (ExceptT, liftEither, runExceptT, throwError)
|
||||
import Control.Monad.IO.Class (liftIO)
|
||||
import Crypto.Random (ChaChaDRG)
|
||||
import Data.Aeson ((.:), (.:?), (.=))
|
||||
@@ -28,12 +28,12 @@ import Data.Text.Encoding (decodeLatin1, encodeUtf8)
|
||||
import Data.Word (Word64)
|
||||
import Network.HTTP.Client (Manager)
|
||||
import qualified Network.HTTP.Client as HTTP
|
||||
import Network.HTTP.Types (hContentType)
|
||||
import Network.HTTP.Types (hContentType, methodPost)
|
||||
import Numeric (readHex)
|
||||
import Simplex.Messaging.Crypto.Secp256k1 (Secp256k1PrivateKey)
|
||||
import Simplex.Messaging.Eth.Address (Address, addressFromPrivateKey)
|
||||
import Simplex.Messaging.Eth.Transaction (Eip1559Tx (..), signEip1559Tx)
|
||||
import Simplex.Messaging.Util (eitherToMaybe, liftError', tshow)
|
||||
import Simplex.Messaging.Util (liftError', tshow)
|
||||
|
||||
data ChainClient = ChainClient
|
||||
{ ccManager :: Manager,
|
||||
@@ -81,8 +81,15 @@ rpc :: ChainClient -> Text -> [J.Value] -> (J.Value -> JT.Parser a) -> ExceptT C
|
||||
rpc ChainClient {ccManager, ccRpcUrl} method params parseResult = do
|
||||
resp <- liftError' (\(e :: HTTP.HttpException) -> ChainError $ tshow e) $ try $ do
|
||||
req <- HTTP.parseRequest ccRpcUrl
|
||||
HTTP.httpLbs req {HTTP.method = "POST", HTTP.requestHeaders = [(hContentType, "application/json")], HTTP.requestBody = HTTP.RequestBodyLBS body} ccManager
|
||||
liftEither $ either (Left . ChainError . T.pack) id $ JT.parseEither response =<< J.eitherDecode (HTTP.responseBody resp)
|
||||
HTTP.httpLbs
|
||||
req
|
||||
{ HTTP.method = methodPost,
|
||||
HTTP.redirectCount = 0,
|
||||
HTTP.requestHeaders = (hContentType, "application/json") : HTTP.requestHeaders req,
|
||||
HTTP.requestBody = HTTP.RequestBodyLBS body
|
||||
}
|
||||
ccManager
|
||||
either (throwError . ChainError . T.pack) liftEither $ JT.parseEither response =<< J.eitherDecode (HTTP.responseBody resp)
|
||||
where
|
||||
body = J.encode $ J.object ["jsonrpc" .= ("2.0" :: Text), "id" .= (1 :: Int), "method" .= method, "params" .= params]
|
||||
response = J.withObject "JSON-RPC response" $ \o -> o .:? "error" >>= maybe (Right <$> (parseResult =<< o .: "result")) rpcError
|
||||
@@ -91,13 +98,12 @@ rpc ChainClient {ccManager, ccRpcUrl} method params parseResult = do
|
||||
quantity :: (Eq a, Num a) => J.Value -> JT.Parser a
|
||||
quantity = J.withText "quantity" $ \t -> case T.stripPrefix "0x" t of
|
||||
Just h | [(n, "")] <- readHex (T.unpack h) -> pure n
|
||||
_ -> fail $ "not a quantity: " <> T.unpack t
|
||||
_ -> fail "not a quantity"
|
||||
|
||||
bytes :: J.Value -> JT.Parser ByteString
|
||||
bytes = J.withText "data" $ maybe (fail "not hex data") pure . fromHex
|
||||
|
||||
fromHex :: Text -> Maybe ByteString
|
||||
fromHex t = T.stripPrefix "0x" t >>= eitherToMaybe . convertFromBase Base16 . encodeUtf8
|
||||
bytes = J.withText "data" $ \t -> case T.stripPrefix "0x" t of
|
||||
Just h | Right bs <- convertFromBase Base16 (encodeUtf8 h) -> pure bs
|
||||
_ -> fail "not hex data"
|
||||
|
||||
hex :: ByteString -> Text
|
||||
hex bs = "0x" <> decodeLatin1 (convertToBase Base16 bs)
|
||||
@@ -109,7 +109,9 @@ recordCalls :: Registration -> [ByteString]
|
||||
recordCalls Registration {rLabel, rTLD, rContactLinks, rChannelLinks} =
|
||||
[setText key ls | (key, ls) <- [("simplex.contact", rContactLinks), ("simplex.channel", rChannelLinks)], not (null ls)]
|
||||
where
|
||||
setText key ls = selector "setText(bytes32,string,string)" <> abiTuple [Static node, Dynamic $ abiBytes key, Dynamic $ abiBytes $ encodeUtf8 $ T.intercalate ";" ls]
|
||||
setText key ls =
|
||||
selector "setText(bytes32,string,string)"
|
||||
<> abiTuple [Static node, Dynamic $ abiBytes key, Dynamic $ abiBytes $ encodeUtf8 $ T.intercalate ";" ls]
|
||||
node = namehash rTLD rLabel
|
||||
|
||||
namehash :: SimplexTLD -> Text -> ByteString
|
||||
|
||||
+5
-5
@@ -1,4 +1,4 @@
|
||||
packages: . ../simplexmq
|
||||
packages: .
|
||||
-- packages: . ../simplexmq
|
||||
-- packages: . ../simplexmq ../direct-sqlcipher ../sqlcipher-simple
|
||||
|
||||
@@ -18,10 +18,10 @@ package cryptostore
|
||||
|
||||
constraints: zip +disable-bzip2 +disable-zstd
|
||||
|
||||
-- source-repository-package
|
||||
-- type: git
|
||||
-- location: https://github.com/simplex-chat/simplexmq.git
|
||||
-- tag: cba13823567d7cd564d8a1f354046174fba375d8
|
||||
source-repository-package
|
||||
type: git
|
||||
location: https://github.com/simplex-chat/simplexmq.git
|
||||
tag: 3bd483209b221f4dc349aa048aec2a6b6a330e4f
|
||||
|
||||
source-repository-package
|
||||
type: git
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# SimpleX names: registration through the sponsor service
|
||||
|
||||
Branch: `ab/names-registration-primitives-sponsored`, with simplexmq branch `ab/name-registration-primitives-sponsored` (a PR into `names`).
|
||||
Branch: `ab/names-registration-primitives-sponsored`, with simplexmq branch `ab/name-registration-primitives-sponsored` (PR #1941 into `names`).
|
||||
|
||||
Contracts: ens-contracts `simplex` with PR #35, and branch `ab/sponsor-sign-duration`, which adds the duration to `RegisterName` (`SimplexController`, `SimplexSponsor`, `SimplexResolver`).
|
||||
Contracts: `SimplexController`, `SimplexSponsor` and `SimplexResolver` from ens-contracts `simplex` with PR #35, and branch `ab/sponsor-sign-duration` (PR #36), which adds the duration to `RegisterName`.
|
||||
|
||||
## Contents
|
||||
|
||||
@@ -26,7 +26,7 @@ This PR adds the functions this needs and tests that run the whole flow against
|
||||
2. The web UI generates a wallet for this one name.
|
||||
3. The web UI shows a redemption code, `SN-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX`. The wallet's key is derived from the code, so whoever has the code controls the name.
|
||||
4. The service registers the name to the wallet's address. The user pays no gas and holds no ETH.
|
||||
5. Later, once the app supports wallets, the user enters the code in the app, and the name moves to the app's wallet.
|
||||
5. Later, once the app supports wallets, the user enters the code in the app, and the name is transferred to the app's wallet.
|
||||
|
||||
## 3. Scope
|
||||
|
||||
@@ -40,17 +40,17 @@ In this PR:
|
||||
|
||||
Not in this PR:
|
||||
|
||||
- the web UI itself, payments, and the service's worker, store, ini and HTTP API;
|
||||
- redeeming the code in the app. After PR #35 a name moves only when its recipient takes it: the code's wallet offers it (`approveWithSig`) and the app's wallet claims it (`claimWithSig`), both relayed by the service.
|
||||
- the web UI itself (simplexmq PR #1942), payments, and the service's worker, database, ini section and HTTP API;
|
||||
- redeeming the code in the app. After PR #35 a name is transferred only with its recipient's consent: the code's wallet offers it (`approveWithSig`) and the app's wallet claims it (`claimWithSig`), both relayed by the service.
|
||||
|
||||
## 4. The flow
|
||||
|
||||
1. **Wallet and code (web UI).** The wallet's entropy is 128 random bits. Its key is the BIP-44 key at `m/44'/60'/0'/0/0` of the 12-word BIP-39 phrase with this entropy, so a BIP-44 wallet app can import it from that phrase. The code is `SN-` and 30 Crockford base32 characters: the entropy and the first 22 bits of its SHA-256.
|
||||
2. **Commitment (web UI).** The web UI chooses a 32-byte secret and builds the registration: label, TLD, owner (the wallet's address), duration, secret, resolver, and the `simplex.contact` and `simplex.channel` records. Records are `setText` calls on the resolver, `simplex.contact` then `simplex.channel`, links joined with `;`, and no call for a key without links. The commitment is `keccak256(abi.encode(Registration))`, as `SimplexController.makeCommitment` computes it.
|
||||
2. **Commitment (web UI).** The web UI generates a 32-byte secret and builds the registration: label, TLD, owner (the wallet's address), duration, secret, resolver, and the `simplex.contact` and `simplex.channel` records. Records are `setText` calls on the resolver, `simplex.contact` then `simplex.channel`, links joined with `;`, and no call for a key without links. The commitment is `keccak256(abi.encode(Registration))`, as `SimplexController.makeCommitment` computes it.
|
||||
3. **Commit (service).** The web UI sends only the commitment, so the name is not revealed. The service sends `commit(commitment)` to the controller.
|
||||
4. **Intent (web UI).** The wallet signs `RegisterName(string label,address owner,uint256 duration,address resolver,bytes[] data,uint256 nonce,uint256 deadline)` under the `SimplexSponsor` EIP-712 domain. The nonce is the owner's count of intents the sponsor has used, so it is 0 for the new wallet. The web UI sends the registration, the nonce, the deadline and the signature.
|
||||
5. **Verify (service).** The service checks that the secret is 32 bytes, the deadline has not passed, and the owner signed the intent.
|
||||
6. **Register (service).** At least `minCommitmentAge` after the commit, the service sends `registerWithCredit(registration, nonce, deadline, signature)` to the sponsor. The sponsor checks the signature and the nonce and spends the registrar's allowance, and the controller registers the name to the owner with its records.
|
||||
6. **Register (service).** At least `minCommitmentAge` after the commit, the service sends `registerWithCredit(registration, nonce, deadline, signature)` to the sponsor. The sponsor checks the deadline, the nonce and the signature, spends the registrar's allowance, and the controller registers the name to the owner with its records.
|
||||
|
||||
Every transaction is first dry-run with `eth_estimateGas`; if it reverts, nothing is sent. The service signs EIP-1559 transactions with its registrar key: gas limit from the estimate, nonce from the registrar's pending transaction count, priority fee from `eth_maxPriorityFeePerGas`, maximum fee twice the latest base fee plus the priority fee.
|
||||
|
||||
@@ -78,12 +78,12 @@ Test mocks:
|
||||
|
||||
- the `commit` calldata of a registration's commitment, and the `registerWithCredit` calldata with and without records, against expected bytes;
|
||||
- verification fails for an intent signed by another wallet, an intent past its deadline, and a secret that is not 32 bytes;
|
||||
- a reverted dry run sends nothing;
|
||||
- nothing is sent when the dry run reverts;
|
||||
- the whole flow: a new wallet, its code read back to the same wallet, the commit, the signed intent, its verification, and the registration, each transaction checked against the expected bytes sent to the controller and the sponsor.
|
||||
|
||||
`tests/BadgeTests.hs`: name code vectors, and a code with a wrong character does not parse.
|
||||
|
||||
simplexmq `tests/CoreTests/EthCryptoTests.hs`: the `RegisterName` digest, the serialized signature, the recovered address, and every error in `recoverPublicKey`.
|
||||
simplexmq `tests/CoreTests/EthCryptoTests.hs`: an EIP-712 digest, the serialized signature, the recovered address, and every error in `recoverPublicKey`.
|
||||
|
||||
Expected values come from independent implementations:
|
||||
|
||||
@@ -93,8 +93,8 @@ Expected values come from independent implementations:
|
||||
|
||||
Checked once against the contracts, outside CI:
|
||||
|
||||
- with the `ab/sponsor-sign-duration` test fixture, the exact calldata the tests expect registered the name to the owner with both records, and a changed signature byte reverted with `InvalidSignature()`;
|
||||
- on a local hardhat node with the PR #35 contracts, `sendCommit` and `sendRegister` registered the name over JSON-RPC.
|
||||
- with the `ab/sponsor-sign-duration` test fixture, a call with the exact calldata the tests expect registered the name to the owner with both records, and the same call with one signature byte changed reverted with `InvalidSignature()`;
|
||||
- on a local hardhat node with the `ab/sponsor-sign-duration` contracts, `sendCommit` and `sendRegister` registered the name over JSON-RPC.
|
||||
|
||||
Run:
|
||||
|
||||
@@ -105,15 +105,24 @@ $(cabal list-bin -w ghc-9.6.3 test:simplex-chat-test) -m "name codes" -m "badge
|
||||
|
||||
## 7. Left to the plumbing PR
|
||||
|
||||
- Call `verifyRegisterIntent` before `sendRegister`, so that a secret that is not 32 bytes cannot change what the contract decodes.
|
||||
- Check the signed duration and the price against what was paid. The registrar's allowance pays for the duration the owner signs.
|
||||
- Call `verifyRegisterIntent` before `sendRegister`. The sponsor checks no signature, nonce or deadline when the owner is the registrar, and a secret that is not 32 bytes can change what the contract decodes.
|
||||
- Parse the commit request's commitment as exactly 32 bytes. `commitCalldata` appends whatever it is given.
|
||||
- Check the signed duration and the price against what was paid. The sponsor spends the registrar's allowance on the duration the owner signs. Price the order from the label's length in the oracle's price list, not with `rentPriceUSD(label, …)`, which sends the label to the node.
|
||||
- Send `commit` only for a paid order. It costs the registrar gas, and the service cannot check a name the commitment hides.
|
||||
- Accept only the default resolver, bound the size of the links, and cap the gas limit. The client chooses the resolver and the links, the controller calls that resolver with the records, and the registrar pays for the gas the estimate returns.
|
||||
- Validate and lower-case the label. The contract hashes the label as given, and resolution lower-cases it.
|
||||
- Register only the registration whose `makeCommitment` equals the commitment of the paid order the request names, once per order, and reject a commitment that is already in another order. Otherwise anyone can commit registrations of their own and have the sponsor spend the registrar's allowance on them.
|
||||
- Accept only the default resolver, only a contact short link in `simplex.contact` and a channel short link in `simplex.channel`, as the app's `firstNameLink` reads them, bound their size, reject a `;` in a link, and cap the gas limit. The client sets the resolver and the links, the controller calls that resolver with the records, readers split a record into links at `;`, and the registrar pays for the gas the transaction uses, up to the estimate.
|
||||
- Cap the priority fee and the maximum fee per gas. Both are set from the node's replies, and the registrar pays up to the maximum fee for each unit of gas used. Send `registerWithCredit` only while the current fees are within the caps, and replace it at market fees if it is not mined within a block or two: its calldata reveals the name, and once it has been pending for `minCommitmentAge` anyone can commit to the name and register it first.
|
||||
- Accept only a label the web UI accepts: 6 to 63 letters a-z, digits 0-9 and single hyphens between them. The label is signed and committed as sent, so the service cannot normalize it, and resolution looks it up lower-cased.
|
||||
- Take the TLD, `.simplex`, from the configuration, not from the request, and build the EIP-712 domain from the chain id and sponsor of the `ChainClient` passed to `sendRegister`.
|
||||
- Parse the client's signature with v 27 or 28 and a low s, as the sponsor's ECDSA requires. libsecp256k1 also recovers a signer from a signature the sponsor rejects, such as one with a high s.
|
||||
- Reject an intent whose deadline leaves too little time to commit, wait `minCommitmentAge` and register. The web UI sets the deadline from the browser's clock, an hour ahead, and a Bitcoin payment can take longer.
|
||||
- Let the user sign again when a paid order's intent expired unused: the web UI rebuilds the wallet from the code, fetches the order's expired register request from the service, uses its label, links and secret only if its signature verifies under the rebuilt wallet's key for the web UI's own chain, sponsor, resolver, duration and TLD, shows the label and links for the user to confirm, and signs with a new deadline and the owner's sponsor nonce. The commitment covers neither, so it does not change.
|
||||
- Send from one registrar key one transaction at a time, since the nonce is the registrar's pending transaction count.
|
||||
- Wait for the commit's receipt and `minCommitmentAge` before registering.
|
||||
- Bound the size of RPC responses.
|
||||
- Store each signed transaction with its order before sending it. After an ambiguous error, send the same transaction again; replace one that is not mined only at its nonce, with higher fees within the caps. Before a new transaction for an order, check the commitment and the owner's sponsor nonce on chain, and the name only once its commitment has aged.
|
||||
- Reconcile the sponsor's `RegistrarAllowanceSpent` and the controller's registrations with the orders, alert the guardian on a mismatch, and top up the allowance in small steps. Whoever holds a leaked registrar key can register any name they learn to a wallet of their own.
|
||||
- Wait until the controller has the commitment, then `minCommitmentAge`, before registering, and register only while the commitment is more than a margin away from `maxCommitmentAge`; otherwise commit it again after it expires. Anyone can send the same commitment first, and then the registrar's own commit reverts.
|
||||
- Call `sendRegister` only once the commitment has aged, as its dry run already sends the calldata to the node, and keep the label out of payment metadata and logs until the name is registered. The calldata reveals the name, and whoever sees it can commit to the name too. The same holds for any call with the label or its hash, such as `available`, `nameExpires` or `ownerOf`.
|
||||
- Accept only an https or loopback RPC URL, bound the size of RPC responses, reject quantities that overflow their type, and check `eth_chainId` against the configured chain.
|
||||
- Read the revert reason from `error.data` to tell a revert from a failed call, after checking that reth returns it there. A hardhat node does not when the revert comes from the controller through the sponsor.
|
||||
- Keep the RPC URL out of logged errors, as it can contain an API key.
|
||||
- Add `BadgeService.Names.Chain` and `BadgeService.Names.Registration` to the `simplex-badge-service` executable in `simplex-chat.cabal`; this PR builds them only in the test suite.
|
||||
- Pin the simplexmq commit in `cabal.project` and `scripts/nix/sha256map.nix`.
|
||||
- Keep the RPC URL out of logged errors, as it can contain an API key, and bound the node's error messages.
|
||||
- Add `BadgeService.Names.Chain` and `BadgeService.Names.Registration` to the `simplex-badge-service` executable in `simplex-chat.cabal`; this PR adds them only to the test suite.
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"https://github.com/simplex-chat/simplexmq.git"."cba13823567d7cd564d8a1f354046174fba375d8" = "0nhd6zsmqs55b7shh945b0v9pn0qqkpx9djq9dldair82hsmw1if";
|
||||
"https://github.com/simplex-chat/simplexmq.git"."3bd483209b221f4dc349aa048aec2a6b6a330e4f" = "1shv98csvarzyl60388ikhn7bsllrax3cjfwyh93yms07a7f20rv";
|
||||
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
|
||||
"https://github.com/simplex-chat/direct-sqlcipher.git"."2330df3dc8c4b660674d3c02d76bb86f66c2abee" = "11pfj1vqsjdl26nplkr2l6z1xj7cn153qijj5gknmlzq3krn7x1i";
|
||||
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
|
||||
|
||||
@@ -149,8 +149,8 @@ parseNameCode t = do
|
||||
body <- T.stripPrefix nameCodePrefix $ T.toUpper $ T.filter isAlphaNum t
|
||||
vs <- mapM charValue $ T.unpack body
|
||||
guard $ length vs == nameCodeLength
|
||||
let (payload, digest) = foldl' (\acc v -> acc * toInteger base + toInteger v) 0 vs `divMod` (2 ^ nameCodeChecksumBits)
|
||||
guard $ digest == nameCodeChecksum payload
|
||||
let (payload, checksum) = foldl' (\acc v -> acc * toInteger base + toInteger v) 0 vs `divMod` (2 ^ nameCodeChecksumBits)
|
||||
guard $ checksum == nameCodeChecksum payload
|
||||
NameCode <$> eitherToMaybe (mkEntropy $ i2ospOf_ 16 payload)
|
||||
|
||||
formatNameCode :: NameCode -> Text
|
||||
|
||||
+1
-1
@@ -344,7 +344,7 @@ testNameCodeVectors =
|
||||
|
||||
testNameCodeChecksum :: IO ()
|
||||
testNameCodeChecksum =
|
||||
isNothing (parseNameCode "SN-KT45V-59ATD-HCNTT-EZRTA-HT8VT-8A0BV") `shouldBe` True
|
||||
formatNameCode <$> parseNameCode "SN-KT45V-59ATD-HCNTT-EZRTA-HT8VT-8A0BV" `shouldBe` Nothing
|
||||
|
||||
-- entropy and its name code, computed by a separate Python implementation of the format
|
||||
nameCodeVectors :: [(ByteString, T.Text)]
|
||||
|
||||
@@ -72,14 +72,14 @@ fakeApp st req respond = do
|
||||
|
||||
answer :: TVar FakeState -> Text -> [J.Value] -> STM [JT.Pair]
|
||||
answer st method params = do
|
||||
FakeState {fsRevert, fsSent} <- readTVar st
|
||||
s@FakeState {fsRevert, fsSent} <- readTVar st
|
||||
case (method, params) of
|
||||
("eth_estimateGas", _) -> pure $ if fsRevert then reverted else result $ quantity fakeGas
|
||||
("eth_getTransactionCount", _) -> pure $ result $ quantity $ toInteger $ length fsSent
|
||||
("eth_maxPriorityFeePerGas", _) -> pure $ result $ quantity fakeTip
|
||||
("eth_getBlockByNumber", _) -> pure $ result $ J.object ["baseFeePerGas" .= quantity fakeBaseFee]
|
||||
("eth_sendRawTransaction", [J.String raw]) | Just tx <- fromHex raw -> do
|
||||
modifyTVar' st $ \s -> s {fsSent = tx : fsSent}
|
||||
writeTVar st s {fsSent = tx : fsSent}
|
||||
pure $ result $ hex $ keccak256 tx
|
||||
_ -> pure ["error" .= J.object ["code" .= (-32601 :: Int), "message" .= ("method not found" :: Text)]]
|
||||
where
|
||||
|
||||
@@ -24,18 +24,18 @@ import Simplex.Messaging.Eth.Transaction (Eip1559Tx (..), signEip1559Tx)
|
||||
import Simplex.Messaging.SimplexName (SimplexTLD (..))
|
||||
import Test.Hspec
|
||||
|
||||
-- expected encodings and signatures are viem's, against the SimplexController, SimplexSponsor and SimplexResolver ABIs
|
||||
badgeNameRegistrationTests :: Spec
|
||||
badgeNameRegistrationTests = describe "badge service name 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 "refuses an intent signed by another wallet" $ testVerifyOtherSigner g
|
||||
it "refuses an intent past its deadline" $ testVerifyExpired g
|
||||
it "refuses a secret that is not 32 bytes" $ testVerifyShortSecret g
|
||||
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 "sends nothing when the dry run reverts" $ testRevertedDryRun g
|
||||
it "registers a name the client committed to without revealing it" $ testCommitThenRegister g
|
||||
|
||||
@@ -81,7 +81,7 @@ testRevertedDryRun g = withFakeEthNode $ \node -> do
|
||||
testCommitThenRegister :: TVar ChaChaDRG -> IO ()
|
||||
testCommitThenRegister g = withFakeEthNode $ \node -> do
|
||||
w <- newEphemeralWallet g
|
||||
(ewAddress <$> walletFromCode g (redemptionCode w)) `shouldReturn` ewAddress w
|
||||
ewAddress <$> walletFromCode g (redemptionCode w) `shouldReturn` ewAddress w
|
||||
let registration = knownRegistration {rOwner = ewAddress w}
|
||||
commitment = makeCommitment registration
|
||||
commitTx <- expectedTransaction g 0 controller $ commitCalldata commitment
|
||||
|
||||
Reference in new issue
Block a user