Skip to content

Commit

Permalink
Calculate coordination seed
Browse files Browse the repository at this point in the history
  • Loading branch information
lukasz-zimnoch committed Nov 24, 2023
1 parent cf07e7a commit ff636e4
Show file tree
Hide file tree
Showing 7 changed files with 176 additions and 21 deletions.
13 changes: 13 additions & 0 deletions pkg/chain/ethereum/ethereum.go
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,19 @@ func (bc *baseChain) GetBlockNumberByTimestamp(
return block.NumberU64(), nil
}

// GetBlockHashByNumber gets the block hash for the given block number.
func (bc *baseChain) GetBlockHashByNumber(blockNumber uint64) (
[32]byte,
error,
) {
block, err := bc.blockByNumber(blockNumber)
if err != nil {
return [32]byte{}, fmt.Errorf("cannot get block: [%v]", err)
}

return block.Hash(), nil
}

// currentBlock fetches the current block.
func (bc *baseChain) currentBlock() (*types.Block, error) {
currentBlockNumber, err := bc.blockCounter.CurrentBlock()
Expand Down
2 changes: 2 additions & 0 deletions pkg/tbtc/chain.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,8 @@ type Chain interface {
// If the aforementioned is not possible, it tries to return the closest
// possible block.
GetBlockNumberByTimestamp(timestamp uint64) (uint64, error)
// GetBlockHashByNumber gets the block hash for the given block number.
GetBlockHashByNumber(blockNumber uint64) ([32]byte, error)

sortition.Chain
GroupSelectionChain
Expand Down
37 changes: 37 additions & 0 deletions pkg/tbtc/chain_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ type localChain struct {
blocksByTimestampMutex sync.Mutex
blocksByTimestamp map[uint64]uint64

blocksHashesByNumberMutex sync.Mutex
blocksHashesByNumber map[uint64][32]byte

pastDepositRevealedEventsMutex sync.Mutex
pastDepositRevealedEvents map[[32]byte][]*DepositRevealedEvent

Expand Down Expand Up @@ -105,6 +108,39 @@ func (lc *localChain) setBlockNumberByTimestamp(timestamp uint64, block uint64)
lc.blocksByTimestamp[timestamp] = block
}

func (lc *localChain) GetBlockHashByNumber(blockNumber uint64) (
[32]byte,
error,
) {
lc.blocksHashesByNumberMutex.Lock()
defer lc.blocksHashesByNumberMutex.Unlock()

blockHash, ok := lc.blocksHashesByNumber[blockNumber]
if !ok {
return [32]byte{}, fmt.Errorf("block not found")
}

return blockHash, nil
}

func (lc *localChain) setBlockHashByNumber(
blockNumber uint64,
blockHashString string,
) {
lc.blocksHashesByNumberMutex.Lock()
defer lc.blocksHashesByNumberMutex.Unlock()

blockHashBytes, err := hex.DecodeString(blockHashString)
if err != nil {
panic(err)
}

var blockHash [32]byte
copy(blockHash[:], blockHashBytes)

lc.blocksHashesByNumber[blockNumber] = blockHash
}

func (lc *localChain) OperatorToStakingProvider() (chain.Address, bool, error) {
panic("unsupported")
}
Expand Down Expand Up @@ -823,6 +859,7 @@ func ConnectWithKey(
),
wallets: make(map[[20]byte]*WalletChainData),
blocksByTimestamp: make(map[uint64]uint64),
blocksHashesByNumber: make(map[uint64][32]byte),
pastDepositRevealedEvents: make(map[[32]byte][]*DepositRevealedEvent),
depositSweepProposalValidations: make(map[[32]byte]bool),
pendingRedemptionRequests: make(map[[32]byte]*RedemptionRequest),
Expand Down
80 changes: 62 additions & 18 deletions pkg/tbtc/coordination.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ package tbtc

import (
"context"
"crypto/sha256"
"fmt"
"github.com/keep-network/keep-core/pkg/bitcoin"
"github.com/keep-network/keep-core/pkg/chain"
"github.com/keep-network/keep-core/pkg/generator"
"github.com/keep-network/keep-core/pkg/net"
Expand All @@ -29,6 +31,11 @@ const (
// coordination window.
coordinationDurationBlocks = coordinationActivePhaseDurationBlocks +
coordinationPassivePhaseDurationBlocks
// coordinationSafeBlockShift is the number of blocks by which the
// coordination block is shifted to obtain a safe block whose 32-byte
// hash can be used as an ingredient for the coordination seed, computed
// for the given coordination window.
coordinationSafeBlockShift = 32
)

// errCoordinationExecutorBusy is an error returned when the coordination
Expand Down Expand Up @@ -138,9 +145,7 @@ func (cft CoordinationFaultType) String() string {

// coordinationFault represents a single coordination fault.
type coordinationFault struct {
// culprit is the address of the operator that is responsible for the fault.
culprit chain.Address
// faultType is the type of the fault.
culprit chain.Address // address of the operator responsible for the fault
faultType CoordinationFaultType
}

Expand Down Expand Up @@ -200,7 +205,11 @@ func (cr *coordinationResult) String() string {
type coordinationExecutor struct {
lock *semaphore.Weighted

signers []*signer // TODO: Do we need whole signers?
chain Chain

coordinatedWallet wallet
membersIndexes []group.MemberIndex

broadcastChannel net.BroadcastChannel
membershipValidator *group.MembershipValidator
protocolLatch *generator.ProtocolLatch
Expand All @@ -209,29 +218,34 @@ type coordinationExecutor struct {
// newCoordinationExecutor creates a new coordination executor for the
// given wallet.
func newCoordinationExecutor(
signers []*signer,
chain Chain,
coordinatedWallet wallet,
membersIndexes []group.MemberIndex,
broadcastChannel net.BroadcastChannel,
membershipValidator *group.MembershipValidator,
protocolLatch *generator.ProtocolLatch,
) *coordinationExecutor {
return &coordinationExecutor{
lock: semaphore.NewWeighted(1),
signers: signers,
chain: chain,
coordinatedWallet: coordinatedWallet,
membersIndexes: membersIndexes,
broadcastChannel: broadcastChannel,
membershipValidator: membershipValidator,
protocolLatch: protocolLatch,
}
}

// wallet returns the wallet this executor is responsible for.
func (ce *coordinationExecutor) wallet() wallet {
// All signers belong to one wallet. Take that wallet from the
// first signer.
return ce.signers[0].wallet
// walletPublicKeyHash returns the 20-byte public key hash of the
// coordinated wallet.
func (ce *coordinationExecutor) walletPublicKeyHash() [20]byte {
return bitcoin.PublicKeyHash(ce.coordinatedWallet.publicKey)
}

// coordinate executes the coordination procedure for the given coordination
// window.
//
// TODO: Add logging.
func (ce *coordinationExecutor) coordinate(
window *coordinationWindow,
) (*coordinationResult, error) {
Expand All @@ -240,18 +254,48 @@ func (ce *coordinationExecutor) coordinate(
}
defer ce.lock.Release(1)

// TODO: Implement coordination logic. Remember about:
// - Setting up the right context
// - Using the protocol latch
// - Using the membership validator
// Example result:
ce.protocolLatch.Lock()
defer ce.protocolLatch.Unlock()

coordinationSeed, err := ce.coordinationSeed(window)
if err != nil {
return nil, fmt.Errorf("failed to compute coordination seed: [%v]", err)
}

fmt.Printf("coordinationSeed: %v\n", coordinationSeed)

// TODO: Implement the rest of the coordination procedure.
result := &coordinationResult{
wallet: ce.wallet(),
wallet: ce.coordinatedWallet,
window: window,
leader: ce.wallet().signingGroupOperators[0],
leader: ce.coordinatedWallet.signingGroupOperators[0],
proposal: &noopProposal{},
faults: nil,
}

return result, nil
}

// coordinationSeed computes the coordination seed for the given coordination
// window.
func (ce *coordinationExecutor) coordinationSeed(
window *coordinationWindow,
) ([32]byte, error) {
walletPublicKeyHash := ce.walletPublicKeyHash()

safeBlockNumber := window.coordinationBlock - coordinationSafeBlockShift
safeBlockHash, err := ce.chain.GetBlockHashByNumber(safeBlockNumber)
if err != nil {
return [32]byte{}, fmt.Errorf(
"failed to get safe block hash: [%v]",
err,
)
}

return sha256.Sum256(
append(
walletPublicKeyHash[:],
safeBlockHash[:]...,
),
), nil
}
47 changes: 47 additions & 0 deletions pkg/tbtc/coordination_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package tbtc

import (
"context"
"encoding/hex"
"testing"
"time"

Expand Down Expand Up @@ -116,3 +117,49 @@ func TestWatchCoordinationWindows(t *testing.T) {
int(receivedWindows[1].coordinationBlock),
)
}

func TestCoordinationExecutor_CoordinationSeed(t *testing.T) {
window := newCoordinationWindow(900)

localChain := Connect()

localChain.setBlockHashByNumber(
window.coordinationBlock-32,
"1322996cbcbc38fc924a46f4df5f9064279d3ab43396e58386dac9b87440d64f",
)

// Uncompressed public key corresponding to the 20-byte public key hash:
// aa768412ceed10bd423c025542ca90071f9fb62d.
publicKeyHex, err := hex.DecodeString(
"0471e30bca60f6548d7b42582a478ea37ada63b402af7b3ddd57f0c95bb6843175" +
"aa0d2053a91a050a6797d85c38f2909cb7027f2344a01986aa2f9f8ca7a0c289",
)
if err != nil {
t.Fatal(err)
}

coordinatedWallet := wallet{
// Set only relevant fields.
publicKey: unmarshalPublicKey(publicKeyHex),
}

executor := &coordinationExecutor{
chain: localChain,
coordinatedWallet: coordinatedWallet,
}

seed, err := executor.coordinationSeed(window)
if err != nil {
t.Fatal(err)
}

// Expected seed is sha256(wallet_public_key_hash | safe_block_hash).
expectedSeed := "e55c779d6d83183409ddc90c6cd5130567f0593349a9c82494b402048ec2d03d"

testutils.AssertStringsEqual(
t,
"coordination seed",
expectedSeed,
hex.EncodeToString(seed[:]),
)
}
11 changes: 10 additions & 1 deletion pkg/tbtc/node.go
Original file line number Diff line number Diff line change
Expand Up @@ -370,8 +370,17 @@ func (n *node) getCoordinationExecutor(
len(signers),
)

// The coordination executor does not need access to signers' key material.
// It is enough to pass only their member indexes.
membersIndexes := make([]group.MemberIndex, len(signers))
for i, s := range signers {
membersIndexes[i] = s.signingGroupMemberIndex
}

executor := newCoordinationExecutor(
signers,
n.chain,
wallet,
membersIndexes,
broadcastChannel,
membershipValidator,
n.protocolLatch,
Expand Down
7 changes: 5 additions & 2 deletions pkg/tbtc/node_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,13 @@ func TestNode_GetCoordinationExecutor(t *testing.T) {
t,
"signers count",
1,
len(executor.signers),
len(executor.membersIndexes),
)

if !reflect.DeepEqual(signer, executor.signers[0]) {
if !reflect.DeepEqual(
signer.signingGroupMemberIndex,
executor.membersIndexes[0],
) {
t.Errorf("executor holds an unexpected signer")
}

Expand Down

0 comments on commit ff636e4

Please sign in to comment.