BLS Aggregated Committed Seals + ParentCommittedSeal
Description
This PR adds:
- BLS feature which enables aggregate CommittedSeal signatures
- New field ParentCommittedSeal to store CommittedSeal of the parent block
- Backward compatibility for the feature of both PAS + BLS
How to start:
-
ECDSA mode:
Same as before
-
BLS mode:
- Create dir:
./polygon-edge secrets init --data-dir test-chain-1 --ecdsa --bls --network - Create genesis.json same as before just add a flag --ibft-validator-type bls
- Start validators
- Create dir:
Changes include
- [ ] Bugfix (non-breaking change that solves an issue)
- [ ] Hotfix (change that solves an urgent issue, and requires immediate attention)
- [x] New feature (non-breaking change that adds functionality)
- [x] Breaking change (change that is not backwards-compatible and/or changes current functionality)
Breaking changes
This PR introduces breaking changes. Clients will have an option to continue running with ECDSA or to switch to BLS
-
How to continue running with ECDSA
To continue running with ECDSA everything you need to do is explained in this guide
-
How to switch to BLS (POA -> POA)
- Stop all nodes
- Add BLS keys for each validators
./polygon-edge secrets init --data-dir test-chain-1 --ecdsa=false --network=false --bls - Create fork into genesis.json
./polygon-edge ibft switch --chain ./genesis.json --type PoA --ibft-validator-type bls --ibft-validators-prefix-path test-chain- --from 100 - Start the nodes
-
How to switch to BLS (POA -> POS)
- Stop all nodes
- Add BLS keys for each validators
./polygon-edge secrets init --data-dir test-chain-1 --ecdsa=false --network=false --bls - Create fork into genesis.json
./polygon-edge ibft switch --chain ./genesis.json --type PoS --ibft-validator-type bls --deployment 50 --from 200 - Start the nodes
- Register BLS Keys
Once latest block height reaches deployment height, run register-blskey script in staking-contract-repo
- Add BLS_PUBLIC_KEY in .env for each validator
BLS_PUBLIC_KEY=<VALIDATOR_X_BLS_PUBLIC_KEY> - Execute script for every each validator
npm run register-blskey
- Add BLS_PUBLIC_KEY in .env for each validator
-
How to switch to BLS (POS -> POS) same steps as for How to switch to BLS (POA -> POS)
Checklist
- [x] I have assigned this PR to myself
- [x] I have added at least 1 reviewer
- [x] I have added the relevant labels
- [x] I have updated the official documentation
- [x] I have added sufficient documentation in code
Testing
- [ ] I have tested this code with the official test suite
- [x] I have tested this code manually
Manual tests
Because of the complexity of writing e2e test, we have a full list of regression tests. All tests passed on commit eed3dc5ab1b9a40c94f158642a6c6d6be569403f. After this PR gets enough approves, WE NEED TO DO ANOTHER ROUND OF REGRESSION TESTS
Documentation update
https://github.com/maticnetwork/matic-docs/pull/1066
Additional comments
We have PR which adds BLS to staking contract repo https://github.com/0xPolygon/staking-contracts/pull/26
Fix EDGE-667, EDGE-668, EDGE-669, EDGE-670
@Kourin1996
Please pull in the latest develop and resolve any conflicts 🙏
I've started the 4 node network in bls mode and i am seeing errors. Can not reproduce it everytime but 3/5 runs showed the error.
How to reproduce it
- Start the network
go run main.go secrets init --data-dir test-chain-1 --ecdsa --bls --network
go run main.go secrets init --data-dir test-chain-2 --ecdsa --bls --network
go run main.go secrets init --data-dir test-chain-3 --ecdsa --bls --network
go run main.go secrets init --data-dir test-chain-4 --ecdsa --bls --network
polygon-sdk % go run main.go genesis --consensus ibft --ibft-validators-prefix-path test-chain- --bootnode /ip4/127.0.0.1/tcp/10001/p2p/<node_id> --bootnode /ip4/127.0.0.1/tcp/20001/p2p/<node_id> --premine=0x8dDd35c2f48223f1f29CeEbF383D445c506D42eA:100000000000000000000000 --premine=16Uiu2HAmEvm286e4LXAjDcLRXXBs4MJF2b67eX1wjrJ1qPLPXHKA:100000000000000000000000 --premine=0xe217c88cD3517198F25F3f2c154cBdFFe40E0499:100000000000000000000000 --premine=0x41a387AB2439C7cf6e0ed7B2E9Fe23cdc69036db:100000000000000000000000 --premine=0x30ea4435167Ee91f9f874b5a894F3282A956C3FF:100000000000000000000000 --ibft-validator-type bls
go run main.go server --data-dir ./test-chain-1 --chain genesis.json --grpc :10000 --libp2p :10001 --jsonrpc :10002 --seal
go run main.go server --data-dir ./test-chain-2 --chain genesis.json --grpc :20000 --libp2p :20001 --jsonrpc :20002 --seal
go run main.go server --data-dir ./test-chain-3 --chain genesis.json --grpc :30000 --libp2p :30001 --jsonrpc :30002 --seal
go run main.go server --data-dir ./test-chain-4 --chain genesis.json --grpc :40000 --libp2p :40001 --jsonrpc :40002 --seal
- Leave network running for a few minutes
Errors:

I've started the 3 node network BLS mode and sent tx using metamask.
I am getting same error everytime:
2022-08-16T21:50:21.219+0200 [ERROR] polygon.ibft: cannot write block: err="unauthorized proposer"
I've started 4 node network (tried with both bls/ecds) and done stress testing using https://github.com/mrwillis/eth-txn-spam
Consol showing a lot of errors which is strange because on develop branch with the same setup it passes

When i want to do migration from ECDS -> BLS (with the ECDSA started on develop branch), after i finish all steps and want to restart nodes i get an error
genesis file does not match current genesis
Also the issue exists if you just switch from develop to this branch and restart nodes (ECDSA -> ECDSA)
Codecov Report
Merging #649 (7546da8) into develop (74ab62e) will increase coverage by
3.67%. The diff coverage is62.01%.
@@ Coverage Diff @@
## develop #649 +/- ##
===========================================
+ Coverage 48.81% 52.48% +3.67%
===========================================
Files 112 130 +18
Lines 15460 17069 +1609
===========================================
+ Hits 7547 8959 +1412
- Misses 7279 7466 +187
- Partials 634 644 +10
| Impacted Files | Coverage Δ | |
|---|---|---|
| blockchain/blockchain.go | 51.62% <0.00%> (ø) |
|
| chain/chain.go | 28.69% <0.00%> (ø) |
|
| consensus/ibft/consensus_backend.go | 0.00% <0.00%> (ø) |
|
| consensus/ibft/messages.go | 0.00% <0.00%> (ø) |
|
| consensus/ibft/operator_service.go | 0.00% <0.00%> (-54.81%) |
:arrow_down: |
| consensus/ibft/verifier.go | 0.00% <0.00%> (ø) |
|
| secrets/secrets.go | 100.00% <ø> (ø) |
|
| state/executor.go | 3.96% <0.00%> (-0.06%) |
:arrow_down: |
| types/rlp_unmarshal.go | 20.08% <0.00%> (ø) |
|
| validators/set.go | 82.35% <ø> (ø) |
|
| ... and 39 more |
:mega: We’re building smart automated test selection to slash your CI/CD build times. Learn more
I've not faced the issues @Aleksao998 mentioned above with the latest commit. Could you check on your side again?
I've not faced the issues @Aleksao998 mentioned above with the latest commit. Could you check on your side again?
From eed3dc5ab1b9a40c94f158642a6c6d6be569403f commit i was not able to reproduce any issues while doing regression tests :D We can focus on doing code review now
@Kourin1996
I think the signer package should be reorganized for better encapsulation and responsibility separation. Out of the entire interface only a few methods seem to be relevant to actual signing - the others have more to do with the types.Header struct. By others I mean these + CalculateHeaderHash.
These methods are types.Header manipulations, only sometimes relying on Signer functionalities (or, to be more precise, the KeyManager feature set which is under the hood of Signer). What I'm proposing on to improve this is:
(1) introduce a new type (IstanbulHeader) in the signer package
This type is really just the regular types.Header, but you extend it with additional methods. In fact, these methods would be the ones we mentioned earlier (of Signer). The implementation would look something like this:
// header.go
package signer
type IstanbulHeader types.Header
func (ih *IstanbulHeader) WriteProposerSeal(..., signer Signer) error {
// ...
seal, err := signer.SignSeal(
crypto.Keccak256(hash.Bytes()),
)
// ...
}
What would client code look like ?
Previously:
header, err := i.currentSigner.WriteCommittedSeals(newBlock.Header, committedSealsMap)
After:
ih := signer.IstanbulHeader(regularHeader)
ih.WriteCommittedSeals(signer, committedSealsMap)
// if you need the default type back, then
h := types.Header(ih)
Other methods would be implemented in the same manner, relying on Signer as an argument in the method signature whenever needed. Also, most of the helpers in extra.go would be tied to this type if possible.
(2) Rework the Signer interface:
Since the SignerImpl is defined in the same package as the interface (which defeats the purpose of the interface), the Signer interface needs to be reworked as a combination of
type Signer interface {
// ...
Type() validators.ValidatorType
Address() types.Address
SignIBFTMessage([]byte) ([]byte, error)
ECRecoverFromIBFTMessage([]byte, []byte) (types.Address, error)
}
and some methods of the KeyManager (since existing Signer has wrappers around KeyManager).
Once the interface is established, 2 separate packages called bls and ecdsa can be defined as (sub)packages. Since we never refer to these implementations directly (as they implement the Signer interface), client code that uses signer.Signer would construct the relevant implementations bls.NewSigner() and ecdsa.NewSigner() (both return Signer).
@dbrajovic
(1) introduce a new type (IstanbulHeader) in the signer package This type is really just the regular types.Header, but you extend it with additional methods. In fact, these methods would be the ones we mentioned earlier (of Signer). The implementation would look something like this:
This implementation looks unnatural for me. It's better to define a function that takes header and signer at least.
(2) Rework the Signer interface: Since the SignerImpl is defined in the same package as the interface (which defeats the purpose of the interface), the Signer interface needs to be reworked as a combination of
Do you mean to implement ECDSASigner and BLSSigner that implement Signer interface? In this case, how to implement common flow? Current signer has several flows that are executed in both types. I don't want to implement the same flow in t ECDSASigner and BLSSigner.
@dbrajovic I tried to move the interface of Signer but stopped due to cycle importing. I can define exactly the same interface in both ibft and forks package. But I don't think it's good way