# SUI交互之suins域名注册参数逆向分析及脚本验证（干货）

By [BSD](https://paragraph.com/@bsd) · 2022-11-28

---

SUI交互之suins域名注册参数逆向分析及脚本验证

开发语言：go

Discord交流: [Link](https://discord.gg/q8QK4w3RtE)

Twitter：[@ananpt0](https://twitter.com/ananpt0)

### 项目主页：

[https://www.suins.io/](https://www.suins.io/)

### 手动测试分析：

首先我们手动注册个域名，然后去Sui Explorer查看记录：

![需要2次transaction](https://storage.googleapis.com/papyrus_images/2aee8ccddf533fbf1d593f760a49d4073952772719680c4b3c6ee30cc85c2bdc.png)

需要2次transaction

[https://explorer.sui.io/](https://explorer.sui.io/)

![第一次](https://storage.googleapis.com/papyrus_images/60fe51ffecab738d699b9e4e35be090f17b1ad162a55019982c11f002e6ae19e.png)

第一次

第一次调用了合约0xf6d55ad60e110c1faf2b54c1824df19481536fd5的 base\_controller模块的make\_commitment\_and\_commit函数，该函数我们看到有两个参数，第一个是个地址字符串，第二个是个整型数组。

我们继续分析这两个参数含义，点击base\_controller去到合约详情页查看合约Bytecode,找到make\_commitment\_and\_commit函数定义：

> entry public make\_commitment\_and\_commit(Arg0: &mut BaseController, Arg1: vector, Arg2: &mut TxContext)
> 
> 第一个参数为BaseController的Object ID
> 
> ![](https://storage.googleapis.com/papyrus_images/31267712d77838dec90d917c142034b9b66ea1be2a02cc53835f6594e536e40c.png)
> 
> 第二个参数是个vector，暂时还不知道是怎么生成的，这个时候我们来调试下前端，看这个参数是怎么生成并传给钱包的。
> 
> 在合约Bytecode中我们看下[虚拟机字节码](https://cookbook.starcoin.org/zh/docs/move/move-vm/summary/)，看到make\_commitment有调用keccak256函数，我们去前端js里面搜索下看看，通过搜索我们发现在main.xxx.js中我们找到一个keccak\_256调用：
> 
> ![](https://storage.googleapis.com/papyrus_images/e99362c46e3a75935b2890d1d04983eb9d00eb8312ea4c06c8ab8d17f37ed8f7.png)
> 
> 我们在这一行下断点，然后点击“Register name”按钮试试，成功断下，然后我们可以发现，keccack\_256参数为一个Uint8Array,由申请域名字符串+钱包地址（去除0x后16进制数组）+15位字符串，此时15位字符串我们还不知道是怎么生成的，我们可以通过调用栈回溯来找到生成的地方：
> 
> ![](https://storage.googleapis.com/papyrus_images/73c8dce11e08c5074aaaa1919a2d2d1d0abeda3ef3a3ec3f0d47e1bc3f5d39d2.png)
> 
> 点击A下面那个匿名函数：
> 
> ![](https://storage.googleapis.com/papyrus_images/14acf54fb0528e5763c06b15ae5270a6b79adb9a01b36c7e55315d01bf5b035e.png)
> 
> 我们可以发现这个15位字符串是用过y.We.internet.password()生成的15位随机字符串。
> 
> 所以第一个Transaction两个参数为：BaseController的Object ID，keccak256后的结果int数组。
> 
> 接下来我们看看第二个Transaction:
> 
> 第一步还是查看Sui Explorer记录,找到Bytecode中register函数
> 
> >     entry public register(Arg0: &mut BaseController, Arg1: &mut BaseRegistrar, Arg2: &mut Registry, Arg3: &Configuration, Arg4: vector<u8>, Arg5: address, Arg6: u64, Arg7: vector<u8>, Arg8: Coin<SUI>, Arg9: &mut TxContext)
> >     
> 
> 可以看出前4个参数分别为BaseController，BaseRegistrar（sui、move后缀的地址不一样），Registry，Configuration地址，第5个参数为注册的域名去除后缀，第6个参数为注册钱包地址，第7个参数为注册期限（一年的为365），第8个参数为15位随机字符串（第一个Transaction生成的15位随机字符串），最后一个为SUI Coin地址。
> 
> 到此基本上我们已经完成了参数定义的分析，最后我们用代码来测试看看：
> 
> ### 代码实现：
> 
>     package main
>     
>     import (
>         "encoding/hex"
>         "errors"
>         "fmt"
>         "github.com/coming-chat/go-sui/account"
>         "github.com/coming-chat/go-sui/client"
>         "github.com/coming-chat/go-sui/types"
>         "golang.org/x/crypto/sha3"
>         "golang.org/x/net/context"
>         "math/rand"
>         "net/url"
>         "time"
>     )
>     
>     const DevnetRpcUrl = "https://fullnode.devnet.sui.io"
>     
>     var bytes []byte = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890_")
>     var (//地址通过https://explorer.sui.io查找替换
>         PackageID         = "0xb43d7bd4394766de04ff82b8214aa8814de69a04"
>         BaseController    = "0x50ac890157d0bfda1be5d2e2a0739e75a64281c7"
>         SUIBaseRegistrar  = "0x4e2ce97c434f449785bbdce745d418c08d9f1a7f"//sui后缀
>         MoveBaseRegistrar = "0xdcea9fe45615172f4d2c3c74a4ed76c565014fa"//move后缀
>         Registry          = "0x79af4acd5d1d70fbb29b724b5060c67063cae83c"
>         Configuration     = "0x1ea0f7ae66fb52e4b51dc89fe9b02e3767a9d15f"
>     )
>     
>     func init() {
>         rand.Seed(time.Now().UnixNano())
>     }
>     
>     func RandStr(n int) string {
>         result := make([]byte, n)
>         for i := 0; i < n; i++ {
>             result[i] = bytes[rand.Int31()%63]
>         }
>         return string(result)
>     }
>     
>     func Hash(data ...[]byte) []int {
>         hash := sha3.NewLegacyKeccak256()
>         for _, d := range data {
>             hash.Write(d)
>         }
>         hashBytes := hash.Sum(nil)
>         intSlice := make([]int, len(hashBytes))
>         for i, b := range hashBytes {
>             intSlice[i] = int(b)
>         }
>         return intSlice
>     }
>     
>     func MakeCommitment(domain, address, solt string) []int {
>         tmp := make([]byte, 0)
>         tmp = append(tmp, []byte(domain)...)
>         addr, _ := hex.DecodeString(address)
>         tmp = append(tmp, addr...)
>         tmp = append(tmp, []byte(solt)...)
>         return Hash(tmp)
>     }
>     
>     func MakeCommitmentAndCommit(client *client.Client, ctx context.Context, signer types.Address, commitment []int, gas *types.ObjectId, gasBudget uint64) (*types.TransactionBytes, error) {
>         packageId, _ := types.NewAddressFromHex(PackageID)
>         args := []any{
>             BaseController, commitment,
>         }
>         return client.MoveCall(ctx, signer, *packageId, "base_controller", "make_commitment_and_commit", args, gas, gasBudget)
>     }
>     
>     func MakeCommit(mnemonic, domain string) (*types.Base64Data, string, error) {
>     
>         acc, _ := account.NewAccountWithMnemonic(mnemonic)
>         signer, _ := types.NewAddressFromHex(acc.Address)
>         addr := acc.Address[2:]//去除0x
>         randStr := RandStr(15)
>         hash := MakeCommitment(domain, addr, randStr)
>         suiClient, err := client.Dial(DevnetRpcUrl)
>         if err != nil {
>             return nil, "", err
>         }
>         gasBudget := uint64(100000)
>         txnBytes, err := MakeCommitmentAndCommit(suiClient, context.Background(), *signer, hash, nil, gasBudget)
>         if err != nil {
>             return nil, "", err
>         }
>         signedTxn := txnBytes.SignWith(acc.PrivateKey)
>         txnResponse, err := suiClient.ExecuteTransaction(context.Background(), *signedTxn, types.TxnRequestTypeWaitForLocalExecution)
>         if err != nil {
>             return nil, "", err
>         }
>         return txnResponse.EffectsCert.Effects.Effects.TransactionDigest, randStr, nil
>     }
>     
>     func Register(client *client.Client, ctx context.Context, signer types.Address, register, domain, address string, day int, randStr string, coinID string, gas *types.ObjectId, gasBudget uint64) (*types.TransactionBytes, error) {
>         packageId, _ := types.NewAddressFromHex(PackageID)
>         args := []any{
>             BaseController, register, Registry, Configuration, domain, address, day, randStr, coinID,
>         }
>         return client.MoveCall(ctx, signer, *packageId, "base_controller", "register", args, gas, gasBudget)
>     }
>     
>     func DoRegister(mnemonic, domain, suffix, randStr string) (*types.Base64Data, error) {
>     
>         acc, _ := account.NewAccountWithMnemonic(mnemonic)
>         signer, _ := types.NewAddressFromHex(acc.Address)
>     
>         suiClient, err := client.Dial(DevnetRpcUrl)
>         if err != nil {
>             return nil, err
>         }
>         gasBudget := uint64(1000000)
>         reg := ""
>         if suffix == "sui" {
>             reg = SUIBaseRegistrar
>         } else if suffix == "move" {
>             reg = MoveBaseRegistrar
>         } else {
>             return nil, errors.New("err suffix")
>         }
>         txnBytes, err := Register(suiClient, context.Background(), *signer, reg, domain, acc.Address, 365, randStr, "COIN_OBJECT_ID", nil, gasBudget)//COIN_OBJECT_ID替换为你钱包中SUI Coin对象ID
>         if err != nil {
>             return nil, err
>         }
>         signedTxn := txnBytes.SignWith(acc.PrivateKey)
>         txnResponse, err := suiClient.ExecuteTransaction(context.Background(), *signedTxn, types.TxnRequestTypeWaitForLocalExecution)
>         if err != nil {
>             return nil, err
>         }
>         return txnResponse.EffectsCert.Effects.Effects.TransactionDigest, nil
>     }
>     
>     func main() {
>         domain := ""//你要注册的域名名称
>         suffix := "sui"//sui或者move后缀
>         fmt.Printf("[+][suins]注册域名：%s.%s\n", domain, suffix)
>         mnemonic := ""//钱包助记词
>         if tid, randStr, err := MakeCommit(mnemonic, domain+"."+suffix); err != nil {
>             fmt.Println("[+]", err)
>         } else {
>             fmt.Printf("[+]make_commitment_and_commit done! Check https://explorer.sui.io/transactions/%s\n", url.QueryEscape(tid.String()))
>             if rid, err := DoRegister(mnemonic, domain, suffix, randStr); err != nil {
>                 fmt.Println("[+]", err)
>             } else {
>                 fmt.Printf("[+]register done! Check https://explorer.sui.io/transactions/%s\n", url.QueryEscape(rid.String()))
>             }
>         }
>     }
>     
> 
> ![成功注册](https://storage.googleapis.com/papyrus_images/7d9772e4d11dcd9bfee607646355c049ebb7171cdf0133a1f8cf583f3659d0c9.png)
> 
> 成功注册

---

*Originally published on [BSD](https://paragraph.com/@bsd/sui-suins)*
