Skip to content
This repository has been archived by the owner on May 22, 2023. It is now read-only.

Commit

Permalink
Merge pull request #616 from keep-network/signature-publication-delays
Browse files Browse the repository at this point in the history
Add signer publication delay

We introduce a delay mechanism that forces signers to wait an amount of time 
before publishing the signature for a given keep. The value of the delay is the 
product of the signer index and a constant step (5 minutes). So:

- member 0 doesn't wait and start the publication process immediately
- member 1 waits 5 minutes
- member 2 waits 10 minutes

This change should prevent signers to publish the signature at the same time. 
Publishing at the same time is not optimal as only the first transaction succeeds 
while the second and third revert and burn some gas.
  • Loading branch information
pdyraga authored Nov 23, 2020
2 parents 999e3ba + 9520cbb commit dd08d60
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 13 deletions.
5 changes: 5 additions & 0 deletions pkg/extensions/tbtc/tbtc.go
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,11 @@ func (t *tbtc) getSignerActionDelay(
return 0, err
}

// just in case this function is not invoked in the right context
if signerIndex < 0 {
return 0, fmt.Errorf("signer index is less than zero")
}

return time.Duration(signerIndex) * t.signerActionDelayStep, nil
}

Expand Down
83 changes: 70 additions & 13 deletions pkg/node/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,20 @@ import (

var logger = log.Logger("keep-ecdsa")

const monitorKeepPublicKeySubmissionTimeout = 30 * time.Minute
const retryDelay = 1 * time.Second
const blockConfirmations = uint64(12)
const (
// Determines the delay which should be preserved before retrying
// actions within the key generation and signing process.
retryDelay = 1 * time.Second

// Number of blocks which should elapse before confirming
// the given chain state expectations.
blockConfirmations = uint64(12)

// Used to calculate the publication delay factor for the given signer index
// to avoid all signers publishing the same signature for given keep at the
// same time.
signaturePublicationDelayStep = 5 * time.Minute
)

// Node holds interfaces to interact with the blockchain and network messages
// transport layer.
Expand Down Expand Up @@ -309,6 +320,8 @@ func (n *Node) publishSignature(
digest [32]byte,
signature *ecdsa.Signature,
) error {
n.waitSignaturePublicationDelay(keepAddress)

attemptCounter := 0
for {
attemptCounter++
Expand Down Expand Up @@ -359,11 +372,6 @@ func (n *Node) publishSignature(
// and there are enough confirmations from the chain.
// We are fine, leaving.
if !isAwaitingSignature && n.confirmSignature(keepAddress, digest) {
logger.Infof(
"signature for keep [%s] already submitted: [%+x]",
keepAddress.String(),
digest,
)
return nil
}

Expand Down Expand Up @@ -391,11 +399,6 @@ func (n *Node) publishSignature(
// confirmations from the chain before making a decision about
// leaving the submission process.
if !isAwaitingSignature && n.confirmSignature(keepAddress, digest) {
logger.Infof(
"signature for keep [%s] already submitted: [%+x]",
keepAddress.String(),
digest,
)
return nil
}

Expand All @@ -420,6 +423,60 @@ func (n *Node) publishSignature(
}
}

// waitSignaturePublicationDelay waits a certain amount of time appropriately
// for the given signer index to avoid all signers publishing the same signature
// for given keep at the same time.
func (n *Node) waitSignaturePublicationDelay(
keepAddress common.Address,
) {
signerIndex, err := n.getSignerIndex(keepAddress)
if err != nil {
logger.Errorf(
"could not determine signature publication delay for keep [%s]: "+
"[%v]; the signature publication will not be delayed",
keepAddress.String(),
err,
)
return
}

// just in case this function is not invoked in the right context
if signerIndex < 0 {
logger.Errorf(
"could not determine signature publication delay for keep [%s], "+
"signer index is less than zero; the signature publication "+
"will not be delayed",
keepAddress.String(),
)
return
}

delay := time.Duration(signerIndex) * signaturePublicationDelayStep

logger.Infof(
"waiting [%v] before publishing signature for keep [%s]",
delay,
keepAddress.String(),
)

time.Sleep(delay)
}

func (n *Node) getSignerIndex(keepAddress common.Address) (int, error) {
members, err := n.ethereumChain.GetMembers(keepAddress)
if err != nil {
return -1, err
}

for index, member := range members {
if member == n.ethereumChain.Address() {
return index, nil
}
}

return -1, nil
}

func (n *Node) waitForSignature(
keepAddress common.Address,
digest [32]byte,
Expand Down

0 comments on commit dd08d60

Please sign in to comment.