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

Catch unsupported keep size and threshold in client #415

Merged
merged 12 commits into from
Apr 18, 2020
5 changes: 4 additions & 1 deletion pkg/chain/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,9 +135,12 @@ type BondedECDSAKeep interface {
// an empty slice is returned.
GetPublicKey(keepAddress common.Address) ([]uint8, error)

// GetPublicKey returns keep's members.
// GetMembers returns keep's members.
GetMembers(keepAddress common.Address) ([]common.Address, error)

// GetHonestThreshold returns keep's honest threshold.
GetHonestThreshold(keepAddress common.Address) (uint64, error)

// HasKeyGenerationTimedOut returns whether key generation
// has timed out for the given keep.
HasKeyGenerationTimedOut(keepAddress common.Address) (bool, error)
Expand Down
48 changes: 46 additions & 2 deletions pkg/chain/ethereum/ethereum.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,21 @@ func (ec *EthereumChain) OnBondedECDSAKeepCreated(
Members []common.Address,
Owner common.Address,
Application common.Address,
HonestThreshold *big.Int,
blockNumber uint64,
) {
handler(&eth.BondedECDSAKeepCreatedEvent{
KeepAddress: KeepAddress,
Members: Members,
KeepAddress: KeepAddress,
Members: Members,
HonestThreshold: HonestThreshold.Uint64(),
})
},
func(err error) error {
return fmt.Errorf("watch keep created failed: [%v]", err)
},
nil,
nil,
nil,
)
}

Expand Down Expand Up @@ -155,6 +160,7 @@ func (ec *EthereumChain) OnConflictingPublicKeySubmitted(
func(err error) error {
return fmt.Errorf("keep created callback failed: [%v]", err)
},
nil,
)
}

Expand Down Expand Up @@ -182,6 +188,7 @@ func (ec *EthereumChain) OnSignatureRequested(
func(err error) error {
return fmt.Errorf("keep signature requested callback failed: [%v]", err)
},
nil,
)
}

Expand Down Expand Up @@ -333,31 +340,40 @@ func (ec *EthereumChain) BalanceOf(address common.Address) (*big.Int, error) {
return ec.bondedECDSAKeepFactoryContract.BalanceOf(address)
}

// BlockCounter returns a block counter.
func (ec *EthereumChain) BlockCounter() chain.BlockCounter {
return ec.blockCounter
}

// IsRegisteredForApplication checks if the operator is registered
// as a signer candidate in the factory for the given application.
func (ec *EthereumChain) IsRegisteredForApplication(application common.Address) (bool, error) {
return ec.bondedECDSAKeepFactoryContract.IsOperatorRegistered(
ec.Address(),
application,
)
}

// IsEligibleForApplication checks if the operator is eligible to register
// as a signer candidate for the given application.
func (ec *EthereumChain) IsEligibleForApplication(application common.Address) (bool, error) {
return ec.bondedECDSAKeepFactoryContract.IsOperatorEligible(
ec.Address(),
application,
)
}

// IsStatusUpToDateForApplication checks if the operator's status
// is up to date in the signers' pool of the given application.
func (ec *EthereumChain) IsStatusUpToDateForApplication(application common.Address) (bool, error) {
return ec.bondedECDSAKeepFactoryContract.IsOperatorUpToDate(
ec.Address(),
application,
)
}

// UpdateStatusForApplication updates the operator's status in the signers'
// pool for the given application.
func (ec *EthereumChain) UpdateStatusForApplication(application common.Address) error {
transaction, err := ec.bondedECDSAKeepFactoryContract.UpdateOperatorStatus(
ec.Address(),
Expand All @@ -375,16 +391,19 @@ func (ec *EthereumChain) UpdateStatusForApplication(application common.Address)
return nil
}

// GetKeepCount returns number of keeps.
func (ec *EthereumChain) GetKeepCount() (*big.Int, error) {
return ec.bondedECDSAKeepFactoryContract.GetKeepCount()
}

// GetKeepAtIndex returns the address of the keep at the given index.
func (ec *EthereumChain) GetKeepAtIndex(
keepIndex *big.Int,
) (common.Address, error) {
return ec.bondedECDSAKeepFactoryContract.GetKeepAtIndex(keepIndex)
}

// LatestDigest returns the latest digest requested to be signed.
func (ec *EthereumChain) LatestDigest(keepAddress common.Address) ([32]byte, error) {
keepContract, err := ec.getKeepContract(keepAddress)
if err != nil {
Expand All @@ -394,6 +413,9 @@ func (ec *EthereumChain) LatestDigest(keepAddress common.Address) ([32]byte, err
return keepContract.Digest()
}

// SignatureRequestedBlock returns block number from the moment when a
// signature was requested for the given digest from a keep.
// If a signature was not requested for the given digest, returns 0.
func (ec *EthereumChain) SignatureRequestedBlock(
keepAddress common.Address,
digest [32]byte,
Expand All @@ -411,6 +433,8 @@ func (ec *EthereumChain) SignatureRequestedBlock(
return blockNumber.Uint64(), nil
}

// GetPublicKey returns keep's public key. If there is no public key yet,
// an empty slice is returned.
func (ec *EthereumChain) GetPublicKey(keepAddress common.Address) ([]uint8, error) {
keepContract, err := ec.getKeepContract(keepAddress)
if err != nil {
Expand All @@ -420,6 +444,7 @@ func (ec *EthereumChain) GetPublicKey(keepAddress common.Address) ([]uint8, erro
return keepContract.GetPublicKey()
}

// GetMembers returns keep's members.
func (ec *EthereumChain) GetMembers(
keepAddress common.Address,
) ([]common.Address, error) {
Expand All @@ -431,6 +456,25 @@ func (ec *EthereumChain) GetMembers(
return keepContract.GetMembers()
}

// GetHonestThreshold returns keep's honest threshold.
func (ec *EthereumChain) GetHonestThreshold(
keepAddress common.Address,
) (uint64, error) {
keepContract, err := ec.getKeepContract(keepAddress)
if err != nil {
return 0, err
}

threshold, err := keepContract.HonestThreshold()
if err != nil {
return 0, err
}

return threshold.Uint64(), nil
}

// HasKeyGenerationTimedOut returns whether key generation
// has timed out for the given keep.
func (ec *EthereumChain) HasKeyGenerationTimedOut(
keepAddress common.Address,
) (bool, error) {
Expand Down
5 changes: 3 additions & 2 deletions pkg/chain/event.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import (

// BondedECDSAKeepCreatedEvent is an event emitted on a new keep creation.
type BondedECDSAKeepCreatedEvent struct {
KeepAddress common.Address // keep contract address
Members []common.Address // keep members addresses
KeepAddress common.Address // keep contract address
Members []common.Address // keep members addresses
HonestThreshold uint64
}

// ConflictingPublicKeySubmittedEvent is an event emitted each time when one of
Expand Down
6 changes: 6 additions & 0 deletions pkg/chain/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,12 @@ func (lc *localChain) GetMembers(
panic("implement")
}

func (lc *localChain) GetHonestThreshold(
keepAddress common.Address,
) (uint64, error) {
panic("implement")
}

func (lc *localChain) HasKeyGenerationTimedOut(
keepAddress common.Address,
) (bool, error) {
Expand Down
30 changes: 30 additions & 0 deletions pkg/client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ func Initialize(
requestedSignatures,
event.KeepAddress,
event.Members,
event.HonestThreshold,
)
}(event)
}
Expand Down Expand Up @@ -303,6 +304,11 @@ func checkAwaitingKeyGenerationForKeep(
return err
}

honestThreshold, err := ethereumChain.GetHonestThreshold(keep)
if err != nil {
return err
}

for _, member := range members {
if ethereumChain.Address() == member {
go generateKeyForKeep(
Expand All @@ -314,6 +320,7 @@ func checkAwaitingKeyGenerationForKeep(
requestedSignatures,
keep,
members,
honestThreshold,
)

break
Expand All @@ -332,7 +339,30 @@ func generateKeyForKeep(
requestedSignatures *requestedSignaturesTrack,
keepAddress common.Address,
members []common.Address,
honestThreshold uint64,
) {
if len(members) < 2 {
// TODO: #408 Implement single signer support.
logger.Errorf(
"keep [%s] has [%d] members; only keeps with at least 2 members are supported",
keepAddress.String(),
len(members),
)
return
}

if honestThreshold != uint64(len(members)) {
// TODO: #325 Implement threshold support.
logger.Errorf(
"keep [%s] has honest threshold [%s] and [%d] members; "+
"only keeps with honest threshold same as group size are supported",
keepAddress.String(),
honestThreshold,
len(members),
)
return
}

logger.Infof(
"member [%s] is starting signer generation for keep [%s]...",
ethereumChain.Address().String(),
Expand Down
8 changes: 4 additions & 4 deletions solidity/contracts/BondedECDSAKeep.sol
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ contract BondedECDSAKeep is IBondedECDSAKeep {
address[] internal members;

// Minimum number of honest keep members required to produce a signature.
uint256 honestThreshold;
uint256 public honestThreshold;

// Stake that was required from each keep member on keep creation.
// The value is used for keep members slashing.
Expand Down Expand Up @@ -88,14 +88,14 @@ contract BondedECDSAKeep is IBondedECDSAKeep {
Status internal status;

// Notification that the keep was requested to sign a digest.
event SignatureRequested(bytes32 digest);
event SignatureRequested(bytes32 indexed digest);

// Notification that the submitted public key does not match a key submitted
// by other member. The event contains address of the member who tried to
// submit a public key and a conflicting public key submitted already by other
// member.
event ConflictingPublicKeySubmitted(
address submittingMember,
address indexed submittingMember,
bytes conflictingPublicKey
);

Expand Down Expand Up @@ -123,7 +123,7 @@ contract BondedECDSAKeep is IBondedECDSAKeep {
// `v` to be calculated by increasing recovery id by 27. Please consult the
// documentation about what the particular chain expects.
event SignatureSubmitted(
bytes32 digest,
bytes32 indexed digest,
bytes32 r,
bytes32 s,
uint8 recoveryID
Expand Down
20 changes: 15 additions & 5 deletions solidity/contracts/BondedECDSAKeepFactory.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,18 @@ contract BondedECDSAKeepFactory is
using SafeMath for uint256;

// Notification that a new sortition pool has been created.
event SortitionPoolCreated(address application, address sortitionPool);
event SortitionPoolCreated(
address indexed application,
address sortitionPool
);

// Notification that a new keep has been created.
event BondedECDSAKeepCreated(
address keepAddress,
address indexed keepAddress,
address[] members,
address owner,
address application
address indexed owner,
address indexed application,
uint256 honestThreshold
);

// Holds the address of the bonded ECDSA keep contract that will be used as a
Expand Down Expand Up @@ -311,7 +315,13 @@ contract BondedECDSAKeepFactory is

keeps.push(address(keep));

emit BondedECDSAKeepCreated(keepAddress, members, _owner, application);
emit BondedECDSAKeepCreated(
keepAddress,
members,
_owner,
application,
_honestThreshold
);
}

/// @notice Gets how many keeps have been opened by this contract.
Expand Down
28 changes: 24 additions & 4 deletions solidity/test/BondedECDSAKeepFactoryTest.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ contract("BondedECDSAKeepFactory", async (accounts) => {
const keepOwner = accounts[5]

const groupSize = new BN(members.length)
const threshold = groupSize
const threshold = new BN(groupSize - 1)

const singleBond = new BN(1)
const bond = singleBond.mul(groupSize)
Expand Down Expand Up @@ -567,9 +567,21 @@ contract("BondedECDSAKeepFactory", async (accounts) => {
)
})

it("opens keep with multiple members", async () => {
it("opens keep with multiple members and emits event", async () => {
const blockNumber = await web3.eth.getBlockNumber()

const keepAddress = await keepFactory.openKeep.call(
groupSize,
threshold,
keepOwner,
bond,
stakeLockDuration,
{
from: application,
value: feeEstimate,
}
)

await keepFactory.openKeep(
groupSize,
threshold,
Expand All @@ -592,11 +604,19 @@ contract("BondedECDSAKeepFactory", async (accounts) => {

assert.equal(eventList.length, 1, "incorrect number of emitted events")

const ev = eventList[0].returnValues

assert.equal(ev.keepAddress, keepAddress, "incorrect keep address")
assert.equal(ev.owner, keepOwner, "incorrect keep owner")
assert.equal(ev.application, application, "incorrect application")

assert.sameMembers(
eventList[0].returnValues.members,
ev.members,
[members[0], members[1], members[2]],
"incorrect keep member in emitted event"
"incorrect keep members"
)

expect(ev.honestThreshold).to.eq.BN(threshold, "incorrect threshold")
})

it("opens bonds for keep", async () => {
Expand Down