bnb-chain / tss-lib

Threshold Signature Scheme, for ECDSA and EDDSA
MIT License
790 stars 271 forks source link

Deadlock in signing #213

Closed r4881t closed 2 years ago

r4881t commented 2 years ago

Hi, I am occasionally encountering deadlocks in signing message.

type PartyShare struct {
    PartyID string
    Share   string
}

func SharedPartyUpdater(party tss.Party, msg tss.Message, errCh chan<- *tss.Error) {
    // do not send a message from this party back to itself
    log.Printf("Got a message from %s for %s",
        msg.GetFrom().GetMoniker(),
        party.PartyID().GetMoniker())
    if party.PartyID() == msg.GetFrom() {
        log.Print("Ignored...")
        return
    }

    // Returns the encoded message bytes to send over the wire along with routing information
    bz, _, err := msg.WireBytes()
    if err != nil {
        errCh <- party.WrapError(err)
        return
    }

    pMsg, err := tss.ParseWireMessage(bz, msg.GetFrom(), msg.IsBroadcast())
    if err != nil {
        errCh <- party.WrapError(err)
        return
    }

    // Will only work in local setup
    if _, err := party.Update(pMsg); err != nil {
        errCh <- err
    }
}

func KeySign(
    shares []PartyShare,
    msg *big.Int,
    threshold int) common.SignatureData {

    curve := tss.S256()
    var output common.SignatureData
    partyCount := len(shares)

    partyIDs := make(tss.UnSortedPartyIDs, 0, partyCount)
    parties := make([]*signing.LocalParty, 0, partyCount)

    // get the partyID and their shares
    for _, share := range shares {
        partyId := new(big.Int)
        partyId, ok := partyId.SetString(share.PartyID, 10)
        if !ok {
            panic("SetString: error")
        }
        common.Logger.Infof("Party ID: %d", partyId)
        partyName := partyId.String()
        pID := tss.NewPartyID(partyName, partyName, partyId)
        // The index is -1 until sorted
        common.Logger.Infof("tss.PartyID: %s", pID.String())
        partyIDs = append(partyIDs, pID)

    }
    pIDs := tss.SortPartyIDs(partyIDs)
    ctx := tss.NewPeerContext(pIDs)

    errCh := make(chan *tss.Error, len(pIDs))
    outCh := make(chan tss.Message, len(pIDs))
    endCh := make(chan common.SignatureData, len(pIDs))

    for _, share := range shares {

        partyId := new(big.Int)
        partyId, ok := partyId.SetString(share.PartyID, 10)
        if !ok {
            panic("SetString: error")
        }

        decodedSave := DecodeKeyShareResponse(share.Share)

        params := tss.NewParameters(
            curve,
            ctx,
            pIDs.FindByKey(partyId), // get the sorted PartyID
            partyCount,
            threshold)
        party := signing.NewLocalParty(
            msg,
            params,
            decodedSave,
            outCh,
            endCh).(*signing.LocalParty)

        parties = append(parties, party)

        go func(P *signing.LocalParty) {
            if err := P.Start(); err != nil {
                errCh <- err
            }
        }(party)

    }

    var ended int32
signing:
    for {
        fmt.Printf("ACTIVE GOROUTINES: %d\n", runtime.NumGoroutine())
        select {
        case err := <-errCh:
            common.Logger.Errorf("Error: %s", err)
            break signing

        case message := <-outCh:
            dest := message.GetTo()
            if dest == nil {
                for _, P := range parties {
                    if P.PartyID().Index == message.GetFrom().Index {
                        continue
                    }
                    go SharedPartyUpdater(P, message, errCh)
                }
            } else {
                if dest[0].Index == message.GetFrom().Index {
                    common.Logger.Fatalf("party %d tried to send a message to itself (%d)", dest[0].Index, message.GetFrom().Index)
                }
                go SharedPartyUpdater(parties[dest[0].Index], message, errCh)
            }

        case save := <-endCh:
            if atomic.AddInt32(&ended, 1) == int32(len(pIDs)) {
                common.Logger.Infof("Done. Received save data from %d participants", ended)
                output = save
                break signing
            }
        case <-time.After(1 * time.Minute):
            for _, P := range parties {
                common.Logger.Debug(P.PartyID().String()+" waiting for ", P.WaitingFor())
            }
            common.Logger.Debug("timeout")
            break signing
        }
    }
    return output
}

With debug logs enabled, this is what I get

17:48:49.709  INFO    tss-lib: Party ID: 3 utils.go:280
17:48:49.709  INFO    tss-lib: tss.PartyID: {-1,3} utils.go:284
17:48:49.709  INFO    tss-lib: Party ID: 2 utils.go:280
17:48:49.709  INFO    tss-lib: tss.PartyID: {-1,2} utils.go:284
ACTIVE GOROUTINES: 6
17:48:49.711  INFO    tss-lib: party {1,3}: signing round 1 starting party.go:135
17:48:49.712  INFO    tss-lib: party {0,2}: signing round 1 starting party.go:135
17:48:49.824 DEBUG    tss-lib: party {0,2}: signing round 1 finished party.go:137
ACTIVE GOROUTINES: 7
ACTIVE GOROUTINES: 7
2022/09/13 17:48:49 Got a message from 2 for 2
2022/09/13 17:48:49 Ignored...
2022/09/13 17:48:49 Got a message from 2 for 3
17:48:49.825 DEBUG    tss-lib: party {1,3}: signing round 1 finished party.go:137
ACTIVE GOROUTINES: 6
ACTIVE GOROUTINES: 7
2022/09/13 17:48:49 Got a message from 3 for 2
17:48:49.825 DEBUG    tss-lib: party {1,3} received message: Type: binance.tsslib.ecdsa.signing.SignRound1Message2, From: {0,2}, To: all party.go:154
17:48:49.825 DEBUG    tss-lib: party {1,3} round 1 update: Type: binance.tsslib.ecdsa.signing.SignRound1Message2, From: {0,2}, To: all party.go:156
2022/09/13 17:48:49 Got a message from 3 for 3
17:48:49.825 DEBUG    tss-lib: party {1,3}: signing round 1 update party.go:162
2022/09/13 17:48:49 Ignored...
17:48:49.825 DEBUG    tss-lib: party {0,2} received message: Type: binance.tsslib.ecdsa.signing.SignRound1Message2, From: {1,3}, To: all party.go:154
17:48:49.825 DEBUG    tss-lib: party {0,2} round 1 update: Type: binance.tsslib.ecdsa.signing.SignRound1Message2, From: {1,3}, To: all party.go:156
17:48:49.825 DEBUG    tss-lib: party {0,2}: signing round 1 update party.go:162
17:49:49.820 DEBUG    tss-lib: {1,3} waiting for  [{0,2}] utils.go:361
17:49:49.820 DEBUG    tss-lib: {0,2} waiting for  [{1,3}] utils.go:361
17:49:49.820 DEBUG    tss-lib: timeout utils.go:363
17:49:49.820  INFO    tss-lib: Signed main.go:91
r4881t commented 2 years ago

I figured it out. It's the order in which the Parties pass their share is important. Based on trial and error, the order of []PartyShares should be sorted.

arslanberk29 commented 2 years ago

Hello, I am trying to compile the tss-lib but while I'm running the main.go I'm getting this error. Has anyone ever seen something similar to this error? Also, I have run the tests and all went successful.

image

r4881t commented 1 year ago

Hello @arslanberk29 - The error you're getting failed to verify proofs is different from the deadlock scenario mentioned in this issue. I would suggest you to open another issue with your error and wait for someone to answer.