Anyswap

作者: 雪落无留痕 | 来源:发表于2021-12-20 11:31 被阅读0次

    门限签名

    通过(t, n)门限签名,即可将一个秘密(私钥)分成n份,任意 t+1个成员可以通过多轮交互,在不重构私钥的情况下,生成一个有效的签名。

    目前主要采用GG18门限签名方案,其密码生成阶段需要5轮交互,签名阶段需要10轮交互。

    Anyswap

    Anyswap是去中心化的跨链协议,基于Fusion DCRM技术。Fushion的DCRM(Distributed Control Right Management) 技术主要用来解决跨链的互操作性,主要采用基于MPC的ECDSA和EdDSA门限签名技术, 实现分布式密钥生成和交易签名。

    Anyswap 是Fushion的一个由DCRM节点组成的侧链网络,最开始有5个节点,后续将扩展到10个以上。

    技术架构

    跨链桥

    跨链桥采用的是铸造-销毁模型,以BNB实现BSC -> OEC 跨链为例:

    1. 用户在BSC上发起转账交易:https://bscscan.com/tx/0x159ca1687828dafc5de4439c4192813911dc54f87f0c8c3935702de63d4182ef

    2. mpc地址: 0x63a3d28bb9187809553dd16981c73f498b6b2687在OEC发起Swapin交易: https://www.oklink.com/oec/tx/0xCFBC6859EEA694797FA6ED6C4052FC4C017CCF06F32C6791E2BF889A34E39A93

        function Swapin(bytes32 txhash, address account, uint256 amount) public onlyAuth returns (bool) {
            _mint(account, amount);
            emit LogSwapin(txhash, account, amount);
            return true;
        }
    

    合约信息: https://www.oklink.com/oec/address/0x218c3c3d49d0e7b37aff0d8bb079de36ae61a4c0

    反向操作OEC -> BSC:

    1. 用户在OEC 上发起交易,调用Swapouthttps://www.oklink.com/oec/address/0x218c3c3d49d0e7b37aff0d8bb079de36ae61a4c0

      function Swapout(uint256 amount, address bindaddr) public returns (bool) {
      require(!_vaultOnly, "AnyswapV4ERC20: onlyAuth");
      require(bindaddr != address(0), "AnyswapV3ERC20: address(0x0)");
      _burn(msg.sender, amount);
      emit LogSwapout(msg.sender, bindaddr, amount);
      return true;
      }
      
    1. MPC地址:0x63a3d28bb9187809553dd16981c73f498b6b2687, 在BSC发起转账交易: https://bscscan.com/tx/0x12215f7c44dcacc19e4a6943d03b770dc34db6ea1ce85f9716b0c6b4079507d2

    多链路由

    多链路由支持原生对原生token的跨链,采用流动性池模型。

    源链交易

    用户发起的交易,调用anySwaputUnderlying方法:

    https://bscscan.com/tx/0x7ffb4944ca3d258966b44c541d798946afe64e833ba7cfbabcc9f18d80ac09af

        // Swaps `amount` `token` from this chain to `toChainID` chain with recipient `to` by minting with `underlying`
        function anySwapOutUnderlying(address token, address to, uint amount, uint toChainID) external {
            TransferHelper.safeTransferFrom(AnyswapV1ERC20(token).underlying(), msg.sender, token, amount);
            AnyswapV1ERC20(token).depositVault(amount, msg.sender);
            _anySwapOut(msg.sender, token, to, amount, toChainID);
        }
    

    源码:https://gist.github.com/zhaojun-sh/0df8429d52ae7d71b6d1ff5e8f0050dc#file-anyswaprouterv4-sol-L239

    目标链交易

    MPC地址发起的交易,调用anySwapInAuto 方法:

    https://polygonscan.com/tx/0x0f4aafaa869703525cad458f1c85f850a5ee7d9876a9c208a395bf9df33e2012

       // swaps `amount` `token` in `fromChainID` to `to` on this chainID with `to` receiving `underlying` if possible
        function anySwapInAuto(bytes32 txs, address token, address to, uint amount, uint fromChainID) external onlyMPC {
            _anySwapIn(txs, token, to, amount, fromChainID);
            AnyswapV1ERC20 _anyToken = AnyswapV1ERC20(token);
            address _underlying = _anyToken.underlying();
            if (_underlying != address(0) && IERC20(_underlying).balanceOf(token) >= amount) {
                _anyToken.withdrawVault(to, amount, to);
            }
        }
    

    源码:https://gist.github.com/zhaojun-sh/0df8429d52ae7d71b6d1ff5e8f0050dc#file-anyswaprouterv4-sol-L304

    若目标链流动性不足,则采用铸造方式。

    更新MPC地址

    https://bscscan.com/address/0x2a038e100f8b85df21e4d44121bdbfe0c288a869

    https://polygonscan.com/address/0x2a038e100f8b85df21e4d44121bdbfe0c288a869

    https://ftmscan.com/address/0x2a038e100f8b85df21e4d44121bdbfe0c288a869

        function changeMPC(address newMPC) public onlyMPC returns (bool) {
            require(newMPC != address(0), "AnyswapV3Router: address(0x0)");
            _oldMPC = mpc();
            _newMPC = newMPC;
            _newMPCEffectiveTime = block.timestamp + 2*24*3600;
            emit LogChangeMPC(_oldMPC, _newMPC, _newMPCEffectiveTime, cID());
            return true;
        }
    
    

    安全攻击

       2021年7月12日,Anyswap遭黑客攻击,损失超过787万美元。被攻击原因为两个交易采用相同的R值签名,黑客从而可以反推出MPC账户私钥。
    
        对于签名: 选择随机数$k$,  计算$R = kG, r= R_x, s= k^{-1}(z+ rd_A)$, 对于两个采用相同随机数的签名:$(r, s)$ 和 $(r, s')$,  对应的签名消息分别为:$z$ 和 $z'$,   可以得到:
    

    s-s'=k^{-1}(z-z') \\ k=\frac{z-z'}{s-s'} \\ d_A=\frac{s\cdot k-z}{r}
    d_A 即为可以计算出的私钥。

    漏洞修复:历史的签名会保存在数据库中,新生成的签名随机数判断是否重复。

                https://github.com/anyswap/CrossChain-Router/commit/ac88cc6861d98645fcdc19d8b985689dd54f5c22  
    

    流程分析

    ScanJob

    CrossChain-Bridge 启动时会开始扫描任务:

    // StartScanJob scan job
    func StartScanJob(isServer bool) {
        srcChainCfg := tokens.SrcBridge.GetChainConfig()
        if srcChainCfg.EnableScan && btc.BridgeInstance != nil {
            go btc.BridgeInstance.StartChainTransactionScanJob()
            if srcChainCfg.EnableScanPool {
                go btc.BridgeInstance.StartPoolTransactionScanJob()
            }
            go btc.BridgeInstance.StartSwapHistoryScanJob()
        }
    }
    

    连接BTC运行的节点,

    // StartChainTransactionScanJob scan job
    func (b *Bridge) StartChainTransactionScanJob() {
        go b.StartPoolTransactionScanJob()      // 扫描mempool 的交易
    
        chainName := b.ChainConfig.BlockChain
        log.Infof("[scanchain] start %v scan chain job", chainName)
    
        start, latest := b.getStartAndLatestHeight()    //确定起始扫描到和最新的块
        _ = tools.UpdateLatestScanInfo(b.IsSrc, start)
        log.Infof("[scanchain] start %v scan chain loop from %v latest=%v", chainName, start, latest)
    
        chainCfg := b.GetChainConfig()
        confirmations := *chainCfg.Confirmations
    
        stable := start
        errorSubject := fmt.Sprintf("[scanchain] get %v block failed", chainName)
        scanSubject := fmt.Sprintf("[scanchain] scanned %v block", chainName)
        for {
            latest := tools.LoopGetLatestBlockNumber(b)
            for h := stable + 1; h <= latest; {
                blockHash, err := b.GetBlockHash(h)    // 从节点获取区块
                if err != nil {
                    log.Error(errorSubject, "height", h, "err", err)
                    time.Sleep(retryIntervalInScanJob)
                    continue
                }
                if scannedBlocks.IsBlockScanned(blockHash) {
                    h++
                    continue
                }
                txids, err := b.GetBlockTxids(blockHash)
                if err != nil {
                    log.Error(errorSubject, "height", h, "blockHash", blockHash, "err", err)
                    time.Sleep(retryIntervalInScanJob)
                    continue
                }
                for _, txid := range txids {
                    b.processTransaction(txid)
                }
                scannedBlocks.CacheScannedBlock(blockHash, h)
                log.Info(scanSubject, "blockHash", blockHash, "height", h, "txs", len(txids))
                h++
            }
            if stable+confirmations < latest {
                stable = latest - confirmations    // 对于未固化的块,会重新扫描
                _ = tools.UpdateLatestScanInfo(b.IsSrc, stable)
            }
            time.Sleep(restIntervalInScanJob)
        }
    }
    

    对于扫描到交易,解析后存储到数据库中。

    func (b *Bridge) processSwapin(txid string) {
        if tools.IsSwapExist(txid, PairID, "", true) {
            return
        }
        swapInfo, err := b.verifySwapinTx(PairID, txid, true)
        tools.RegisterSwapin(txid, []*tokens.TxSwapInfo{swapInfo}, []error{err})  //数据存储
    }
    

    存储到mongoDB后的数据为:

    // MgoSwap registered swap
    type MgoSwap struct {
        Key       string     `bson:"_id"` // txid + pairid + bind
        PairID    string     `bson:"pairid"`
        TxID      string     `bson:"txid"`    // 交易id
        TxTo      string     `bson:"txto"`
        TxType    uint32     `bson:"txtype"`   // SwapinTx
        Bind      string     `bson:"bind"`  //绑定到目标链的地址
        Status    SwapStatus `bson:"status"`
        InitTime  int64      `bson:"inittime"`
        Timestamp int64      `bson:"timestamp"`
        Memo      string     `bson:"memo"`
    }
    

    SwapJob

    启动SwapJob 任务:

    // StartSwapJob swap job
    func StartSwapJob() {
        swapinNonces, swapoutNonces := mongodb.LoadAllSwapNonces()
        if tokens.DstNonceSetter != nil {
            tokens.DstNonceSetter.InitNonces(swapinNonces)
        }
        if tokens.SrcNonceSetter != nil {
            tokens.SrcNonceSetter.InitNonces(swapoutNonces)
        }
        for _, pairCfg := range tokens.GetTokenPairsConfig() {
            AddSwapJob(pairCfg)
        }
    
        mongodb.MgoWaitGroup.Add(2)
        go startSwapinSwapJob()
        go startSwapoutSwapJob()
    }
    

    从MongoDB读取MgoSwap进行处理:

    func processSwap(swap *mongodb.MgoSwap, isSwapin bool) (err error) {
        pairID := swap.PairID
        txid := swap.TxID
        bind := swap.Bind
    
        cacheKey := getSwapCacheKey(isSwapin, txid, bind)   // 判断是否已经被处理
        if cachedSwapTasks.Contains(cacheKey) {
            return errAlreadySwapped
        }
    
        res, err := mongodb.FindSwapResult(isSwapin, txid, pairID, bind)  
        if err != nil {
            return err
        }
    
        err = preventReswap(res, isSwapin)
        if err != nil {
            return err
        }
    
        dcrmAddress, err := checkSwapResult(res, isSwapin)
        if err != nil {
            return err
        }
    
        logWorker("swap", "start process swap", "pairID", pairID, "txid", txid, "bind", bind, "status", swap.Status, "isSwapin", isSwapin, "value", res.Value)
    
        srcBridge := tokens.GetCrossChainBridge(isSwapin)
      // 重复进行验证
        swapInfo, err := verifySwapTransaction(srcBridge, pairID, txid, bind, tokens.SwapTxType(swap.TxType))
        if err != nil {
            return fmt.Errorf("[doSwap] reverify swap failed, %w", err)
        }
        if swapInfo.Value.String() != res.Value {
            return fmt.Errorf("[doSwap] reverify swap value mismatch, in db %v != %v", res.Value, swapInfo.Value)
        }
        if !strings.EqualFold(swapInfo.Bind, bind) {
            return fmt.Errorf("[doSwap] reverify swap bind address mismatch, in db %v != %v", bind, swapInfo.Bind)
        }
    
        swapType := getSwapType(isSwapin)
        args := &tokens.BuildTxArgs{
            SwapInfo: tokens.SwapInfo{
                Identifier: params.GetIdentifier(),
                PairID:     pairID,
                SwapID:     txid,
                SwapType:   swapType,
                TxType:     tokens.SwapTxType(swap.TxType),
                Bind:       bind,
                Reswapping: res.Status == mongodb.Reswapping,
            },
            From:        dcrmAddress,
            OriginValue: swapInfo.Value,
        }
    
        return dispatchSwapTask(args)
    }
    

    构建交易,数据结构为:

    // BuildTxArgs struct
    type BuildTxArgs struct {
        SwapInfo    `json:"swapInfo,omitempty"`          // 包含Bind 地址 
        From        string     `json:"from,omitempty"`    //Dcrm 地址
        To          string     `json:"to,omitempty"`      // 
        Value       *big.Int   `json:"value,omitempty"`    // 金额
        OriginValue *big.Int   `json:"originValue,omitempty"`
        SwapValue   *big.Int   `json:"swapvalue,omitempty"`
        Memo        string     `json:"memo,omitempty"`
        Input       *[]byte    `json:"input,omitempty"`
        Extra       *AllExtras `json:"extra,omitempty"`
    }
    

    通过chan 传递 BuildTxArgs继续处理任务:

    func processSwapTask(swapChan <-chan *tokens.BuildTxArgs, dcrmAddress string, isSwapin bool) {
        defer utils.TopWaitGroup.Done()
        for {
            select {
            case <-utils.CleanupChan:
                logWorker("doSwap", "stop process swap task", "isSwapin", isSwapin, "dcrmAddress", dcrmAddress)
                return
            case args := <-swapChan:
                if !strings.EqualFold(args.From, dcrmAddress) || args.SwapType != getSwapType(isSwapin) {
                    logWorkerWarn("doSwap", "ignore swap task as mismatch reason", "isSwapin", isSwapin, "dcrmAddress", dcrmAddress, "args", args)
                    continue
                }
                err := doSwap(args)
                switch {
                case err == nil,
                    errors.Is(err, errAlreadySwapped):
                default:
                    logWorkerError("doSwap", "process failed", err, "pairID", args.PairID, "txid", args.SwapID, "swapType", args.SwapType.String(), "value", args.OriginValue)
                }
            }
        }
    }
    

    开始执行签名过程,若配置文件有DCRM私钥,则直接在本地签名,否则开始进行DCRM MPC 签名:

    func doSwap(args *tokens.BuildTxArgs) (err error) {
        pairID := args.PairID
        txid := args.SwapID
        bind := args.Bind
        swapType := args.SwapType
    
        isSwapin := swapType == tokens.SwapinType
        resBridge := tokens.GetCrossChainBridge(!isSwapin)
    
        cacheKey := getSwapCacheKey(isSwapin, txid, bind)
        err = checkAndUpdateProcessSwapTaskCache(cacheKey)
        if err != nil {
            return err
        }
        logWorker("doSwap", "add swap cache", "pairID", pairID, "txid", txid, "bind", bind, "isSwapin", isSwapin, "value", args.OriginValue)
        isCachedSwapProcessed := false
        defer func() {
            if !isCachedSwapProcessed {
                logWorkerError("doSwap", "delete swap cache", err, "pairID", pairID, "txid", txid, "bind", bind, "isSwapin", isSwapin, "value", args.OriginValue)
                cachedSwapTasks.Remove(cacheKey)
            }
        }()
    
        logWorker("doSwap", "start to process", "pairID", pairID, "txid", txid, "bind", bind, "isSwapin", isSwapin, "value", args.OriginValue)
    
        rawTx, err := resBridge.BuildRawTransaction(args)
        if err != nil {
            logWorkerError("doSwap", "build tx failed", err, "pairID", pairID, "txid", txid, "bind", bind, "isSwapin", isSwapin)
            return err
        }
    
        swapNonce := args.GetTxNonce()
    
        var signedTx interface{}
        var signTxHash string
        tokenCfg := resBridge.GetTokenConfig(pairID)
        for i := 1; i <= 3; i++ { // with retry
            if tokenCfg.GetDcrmAddressPrivateKey() != nil {
                signedTx, signTxHash, err = resBridge.SignTransaction(rawTx, pairID)  // 直接本地签名
            } else {
                signedTx, signTxHash, err = resBridge.DcrmSignTransaction(rawTx, args.GetExtraArgs())  // 采用DCRM签名
            }
            if err == nil {
                break
            }
            logWorkerError("doSwap", "sign tx failed", err, "pairID", pairID, "txid", txid, "bind", bind, "isSwapin", isSwapin, "signCount", i)
            restInJob(retrySignInterval)
        }
        if err != nil {
            return err
        }
    
        // recheck reswap before update db
        res, err := mongodb.FindSwapResult(isSwapin, txid, pairID, bind)
        if err != nil {
            return err
        }
        err = preventReswap(res, isSwapin)
        if err != nil {
            return err
        }
    
        // update database before sending transaction
        matchTx := &MatchTx{
            SwapTx:    signTxHash,
            SwapType:  swapType,
            SwapNonce: swapNonce,
        }
        if args.SwapValue != nil {
            matchTx.SwapValue = args.SwapValue.String()
        } else {
            matchTx.SwapValue = tokens.CalcSwappedValue(pairID, args.OriginValue, isSwapin).String()
        }
        err = updateSwapResult(txid, pairID, bind, matchTx)
        if err != nil {
            logWorkerError("doSwap", "update swap result failed", err, "pairID", pairID, "txid", txid, "bind", bind, "isSwapin", isSwapin)
            return err
        }
        isCachedSwapProcessed = true
    
        err = mongodb.UpdateSwapStatus(isSwapin, txid, pairID, bind, mongodb.TxProcessed, now(), "")
        if err != nil {
            logWorkerError("doSwap", "update swap status failed", err, "pairID", pairID, "txid", txid, "bind", bind, "isSwapin", isSwapin)
            return err
        }
    
        txHash, err := sendSignedTransaction(resBridge, signedTx, args) //广播交易,最终调用:eth_sendRawTransaction
        if err == nil {
            logWorker("doSwap", "send tx success", "pairID", pairID, "txid", txid, "bind", bind, "isSwapin", isSwapin, "swapNonce", swapNonce, "txHash", txHash)
            if txHash != signTxHash {
                logWorkerError("doSwap", "send tx success but with different hash", errSendTxWithDiffHash, "pairID", pairID, "txid", txid, "bind", bind, "isSwapin", isSwapin, "swapNonce", swapNonce, "txHash", txHash, "signTxHash", signTxHash)
                _ = replaceSwapResult(txid, pairID, bind, txHash, matchTx.SwapValue, isSwapin)
            }
        }
        return err
    }
    
    

    进行MPC 签名:

    // DoSign dcrm sign msgHash with context msgContext
    func DoSign(signPubkey string, msgHash, msgContext []string) (keyID string, rsvs []string, err error) {
        if !params.IsDcrmEnabled() {
            return "", nil, errSignIsDisabled
        }
        log.Debug("dcrm DoSign", "msgHash", msgHash, "msgContext", msgContext)
        if signPubkey == "" {
            return "", nil, errSignWithoutPublickey
        }
        for i := 0; i < retrySignLoop; i++ {
            for _, dcrmNode := range allInitiatorNodes {  // 参与MPC的节点
                if err = pingDcrmNode(dcrmNode); err != nil {
                    continue
                }
                signGroupsCount := int64(len(dcrmNode.signGroups))
                // randomly pick first subgroup to sign
                randIndex, _ := rand.Int(rand.Reader, big.NewInt(signGroupsCount))
                startIndex := randIndex.Int64()
                i := startIndex
                for {
                    keyID, rsvs, err = doSignImpl(dcrmNode, i, signPubkey, msgHash, msgContext)
                    if err == nil {
                        return keyID, rsvs, nil
                    }
                    i = (i + 1) % signGroupsCount
                    if i == startIndex {
                        break
                    }
                }
            }
            time.Sleep(2 * time.Second)
        }
        log.Warn("dcrm DoSign failed", "msgHash", msgHash, "msgContext", msgContext, "err", err)
        return "", nil, errDoSignFailed
    }
    

    调用MPC 节点进行签名:

    // Sign call sign
    func Sign(raw, rpcAddr string) (string, error) {
        var result DataResultResp
        err := httpPostTo(&result, rpcAddr, "sign", raw)
        if err != nil {
            return "", wrapPostError("sign", err)
        }
        if result.Status != successStatus {
            return "", newWrongStatusError("sign", result.Status, result.Error)
        }
        return result.Data.Result, nil
    }
    

    AcceptSignJob

    获取需要进行的签名信息:

    func startAcceptProducer() {
        i := 0
        for {
            signInfo, err := dcrm.GetCurNodeSignInfo(maxAcceptSignTimeInterval)   //通过接口调用获取签名
            if err != nil {
                logWorkerError("accept", "getCurNodeSignInfo failed", err)
                time.Sleep(retryInterval)
                continue
            }
            i++
            if i%7 == 0 {
                logWorker("accept", "getCurNodeSignInfo", "count", len(signInfo))
            }
            for _, info := range signInfo {
                if utils.IsCleanuping() {
                    return
                }
                if info == nil { // maybe a dcrm RPC problem
                    continue
                }
                keyID := info.Key
                if cachedAcceptInfos.Contains(keyID) {
                    logWorkerTrace("accept", "ignore cached accept sign info before dispatch", "keyID", keyID)
                    continue
                }
                logWorker("accept", "dispatch accept sign info", "keyID", keyID)
                acceptInfoCh <- info // produce           // 
            }
            if utils.IsCleanuping() {
                return
            }
            time.Sleep(waitInterval)
        }
    }
    

    处理签名信息, 确定是否进行签名:

    func processAcceptInfo(info *dcrm.SignInfoData) {
        defer atomic.AddInt64(&curAcceptRoutines, -1)
    
        keyID := info.Key
        if !checkAndUpdateCachedAcceptInfoMap(keyID) {
            return
        }
        isProcessed := false
        defer func() {
            if !isProcessed {
                cachedAcceptInfos.Remove(keyID)
            }
        }()
    
        args, err := verifySignInfo(info) // 进行签名信息验证,会调用链上全节点的信息
    
        ctx := []interface{}{
            "keyID", keyID,
        }
        if args != nil {
            ctx = append(ctx,
                "identifier", args.Identifier,
                "swaptype", args.SwapType.String(),
                "pairID", args.PairID,
                "swapID", args.SwapID,
                "bind", args.Bind,
            )
        }
    
        switch {
        case errors.Is(err, tokens.ErrTxNotStable),
            errors.Is(err, tokens.ErrTxNotFound),
            errors.Is(err, tokens.ErrRPCQueryError):
            ctx = append(ctx, "err", err)
            logWorkerTrace("accept", "ignore sign", ctx...)
            return
        case errors.Is(err, errIdentifierMismatch):
            ctx = append(ctx, "err", err)
            logWorkerTrace("accept", "discard sign", ctx...)
            isProcessed = true
            return
        case errors.Is(err, errInitiatorMismatch),
            errors.Is(err, errWrongMsgContext),
            errors.Is(err, tokens.ErrUnknownPairID),
            errors.Is(err, tokens.ErrNoBtcBridge):
            ctx = append(ctx, "err", err)
            logWorker("accept", "discard sign", ctx...)
            isProcessed = true
            return
        }
    
        agreeResult := acceptAgree          
        if err != nil {
            logWorkerError("accept", "DISAGREE sign", err, ctx...)
            agreeResult = acceptDisagree
        }
        ctx = append(ctx, "result", agreeResult) 
    
        res, err := dcrm.DoAcceptSign(keyID, agreeResult, info.MsgHash, info.MsgContext)  // 接受签名
        if err != nil {
            ctx = append(ctx, "rpcResult", res)
            logWorkerError("accept", "accept sign job failed", err, ctx...)
        } else {
            logWorker("accept", "accept sign job finish", ctx...)
            isProcessed = true
        }
    }
    

    接受签名后的处理:

    
    // DoAcceptSign accept sign
    func DoAcceptSign(keyID, agreeResult string, msgHash, msgContext []string) (string, error) {
        nonce := uint64(0)
        data := AcceptData{
            TxType:  "ACCEPTSIGN",
            Key:     keyID,
            Accept:  agreeResult,
            MsgHash: msgHash,
            //MsgContext: msgContext, // context is verified on top level
            TimeStamp: common.NowMilliStr(),
        }
        payload, err := json.Marshal(data)
        if err != nil {
            return "", err
        }
        rawTX, err := BuildDcrmRawTx(nonce, payload, defaultDcrmNode.keyWrapper)
        if err != nil {
            return "", err
        }
        return AcceptSign(rawTX)
    }
    

    调用acceptSign接口, 执行MPC签名.

    // AcceptSign call acceptSign
    func AcceptSign(raw string) (string, error) {
        var result DataResultResp
        err := httpPost(&result, "acceptSign", raw)
        if err != nil {
            return "", wrapPostError("acceptSign", err)
        }
        if result.Status != successStatus {
            return "", newWrongStatusError("acceptSign", result.Status, result.Error)
        }
        return result.Data.Result, nil
    }
    

    参考

    https://github.com/anyswap/Anyswap-Audit/blob/master/whitepaper/Anyswap-whitepaper.pdf

    https://anyswap.exchange/#/router

    https://github.com/anyswap/CrossChain-Bridge

    https://github.com/anyswap/mBTC

    https://fusiondev.gitbook.io/fusion/learn/what-is-the-vision-of-fusion/how-does-fusion-and-anyswap-compare

    https://anyswap-faq.readthedocs.io/en/latest/

    https://en.wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm

    https://anyswap.medium.com/anyswap-multichain-router-v3-exploit-statement-6833f1b7e6fb

    相关文章

      网友评论

          本文标题:Anyswap

          本文链接:https://www.haomeiwen.com/subject/adtzfrtx.html