mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -05:00
Implement PoW Chain Deposit Trie in Go (#1285)
This commit is contained in:
@@ -14,6 +14,7 @@ go_library(
|
||||
"//beacon-chain/core/validators:go_default_library",
|
||||
"//beacon-chain/utils:go_default_library",
|
||||
"//proto/beacon/p2p/v1:go_default_library",
|
||||
"//shared/bytes:go_default_library",
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/slices:go_default_library",
|
||||
@@ -39,6 +40,7 @@ go_test(
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
"//shared/ssz:go_default_library",
|
||||
"//shared/trie:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//common:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/core/state/stateutils"
|
||||
v "github.com/prysmaticlabs/prysm/beacon-chain/core/validators"
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
bytesutil "github.com/prysmaticlabs/prysm/shared/bytes"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/slices"
|
||||
@@ -589,16 +590,13 @@ func ProcessValidatorDeposits(
|
||||
}
|
||||
|
||||
func verifyDeposit(beaconState *pb.BeaconState, deposit *pb.Deposit) error {
|
||||
depositData := deposit.DepositData
|
||||
// Verify Merkle proof of deposit and deposit trie root.
|
||||
var receiptRoot [32]byte
|
||||
var merkleLeaf [32]byte
|
||||
copy(receiptRoot[:], beaconState.LatestDepositRootHash32)
|
||||
copy(merkleLeaf[:], depositData)
|
||||
receiptRoot := bytesutil.ToBytes32(beaconState.LatestDepositRootHash32)
|
||||
if ok := trie.VerifyMerkleBranch(
|
||||
merkleLeaf,
|
||||
hashutil.Hash(deposit.DepositData),
|
||||
deposit.MerkleBranchHash32S,
|
||||
params.BeaconConfig().DepositContractTreeDepth,
|
||||
deposit.MerkleTreeIndex,
|
||||
receiptRoot,
|
||||
); !ok {
|
||||
return fmt.Errorf(
|
||||
|
||||
@@ -9,8 +9,9 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/trie"
|
||||
|
||||
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
"github.com/prysmaticlabs/prysm/shared/ssz"
|
||||
)
|
||||
@@ -1217,14 +1218,14 @@ func TestProcessValidatorDeposits_MerkleBranchFailsVerification(t *testing.T) {
|
||||
data = append(data, timestamp...)
|
||||
|
||||
// We then create a merkle branch for the test.
|
||||
branch := [][]byte{}
|
||||
for i := uint64(0); i < params.BeaconConfig().DepositContractTreeDepth; i++ {
|
||||
branch = append(branch, []byte{1})
|
||||
}
|
||||
depositTrie := trie.NewDepositTrie()
|
||||
depositTrie.UpdateDepositTrie(data)
|
||||
branch := depositTrie.GenerateMerkleBranch(0)
|
||||
|
||||
deposit := &pb.Deposit{
|
||||
DepositData: data,
|
||||
MerkleBranchHash32S: branch,
|
||||
MerkleTreeIndex: 0,
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Body: &pb.BeaconBlockBody{
|
||||
@@ -1232,7 +1233,7 @@ func TestProcessValidatorDeposits_MerkleBranchFailsVerification(t *testing.T) {
|
||||
},
|
||||
}
|
||||
beaconState := &pb.BeaconState{
|
||||
LatestDepositRootHash32: []byte{0},
|
||||
LatestDepositRootHash32: []byte{},
|
||||
}
|
||||
want := "merkle branch of deposit root did not verify"
|
||||
if _, err := ProcessValidatorDeposits(
|
||||
@@ -1283,22 +1284,15 @@ func TestProcessValidatorDeposits_ProcessDepositHelperFuncFails(t *testing.T) {
|
||||
data = append(data, value...)
|
||||
data = append(data, timestamp...)
|
||||
|
||||
// We then create a merkle branch for the test and derive its root.
|
||||
branch := [][]byte{}
|
||||
var powReceiptRoot [32]byte
|
||||
copy(powReceiptRoot[:], data)
|
||||
for i := uint64(0); i < params.BeaconConfig().DepositContractTreeDepth; i++ {
|
||||
branch = append(branch, []byte{1})
|
||||
if i%2 == 0 {
|
||||
powReceiptRoot = hashutil.Hash(append(branch[i], powReceiptRoot[:]...))
|
||||
} else {
|
||||
powReceiptRoot = hashutil.Hash(append(powReceiptRoot[:], branch[i]...))
|
||||
}
|
||||
}
|
||||
// We then create a merkle branch for the test.
|
||||
depositTrie := trie.NewDepositTrie()
|
||||
depositTrie.UpdateDepositTrie(data)
|
||||
branch := depositTrie.GenerateMerkleBranch(0)
|
||||
|
||||
deposit := &pb.Deposit{
|
||||
DepositData: data,
|
||||
MerkleBranchHash32S: branch,
|
||||
MerkleTreeIndex: 0,
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Body: &pb.BeaconBlockBody{
|
||||
@@ -1314,10 +1308,11 @@ func TestProcessValidatorDeposits_ProcessDepositHelperFuncFails(t *testing.T) {
|
||||
},
|
||||
}
|
||||
balances := []uint64{0}
|
||||
root := depositTrie.Root()
|
||||
beaconState := &pb.BeaconState{
|
||||
ValidatorRegistry: registry,
|
||||
ValidatorBalances: balances,
|
||||
LatestDepositRootHash32: powReceiptRoot[:],
|
||||
LatestDepositRootHash32: root[:],
|
||||
Slot: currentSlot,
|
||||
GenesisTime: uint64(genesisTime),
|
||||
}
|
||||
@@ -1368,22 +1363,15 @@ func TestProcessValidatorDeposits_ProcessCorrectly(t *testing.T) {
|
||||
data = append(data, value...)
|
||||
data = append(data, timestamp...)
|
||||
|
||||
// We then create a merkle branch for the test and derive its root.
|
||||
branch := [][]byte{}
|
||||
var powReceiptRoot [32]byte
|
||||
copy(powReceiptRoot[:], data)
|
||||
for i := uint64(0); i < params.BeaconConfig().DepositContractTreeDepth; i++ {
|
||||
branch = append(branch, []byte{1})
|
||||
if i%2 == 0 {
|
||||
powReceiptRoot = hashutil.Hash(append(branch[i], powReceiptRoot[:]...))
|
||||
} else {
|
||||
powReceiptRoot = hashutil.Hash(append(powReceiptRoot[:], branch[i]...))
|
||||
}
|
||||
}
|
||||
// We then create a merkle branch for the test.
|
||||
depositTrie := trie.NewDepositTrie()
|
||||
depositTrie.UpdateDepositTrie(data)
|
||||
branch := depositTrie.GenerateMerkleBranch(0)
|
||||
|
||||
deposit := &pb.Deposit{
|
||||
DepositData: data,
|
||||
MerkleBranchHash32S: branch,
|
||||
MerkleTreeIndex: 0,
|
||||
}
|
||||
block := &pb.BeaconBlock{
|
||||
Body: &pb.BeaconBlockBody{
|
||||
@@ -1397,10 +1385,11 @@ func TestProcessValidatorDeposits_ProcessCorrectly(t *testing.T) {
|
||||
},
|
||||
}
|
||||
balances := []uint64{0}
|
||||
root := depositTrie.Root()
|
||||
beaconState := &pb.BeaconState{
|
||||
ValidatorRegistry: registry,
|
||||
ValidatorBalances: balances,
|
||||
LatestDepositRootHash32: powReceiptRoot[:],
|
||||
LatestDepositRootHash32: root[:],
|
||||
Slot: currentSlot,
|
||||
GenesisTime: uint64(genesisTime),
|
||||
}
|
||||
|
||||
@@ -240,14 +240,14 @@ func (s *Server) Send(msg proto.Message, peer Peer) {
|
||||
s.Broadcast(msg)
|
||||
}
|
||||
|
||||
// Broadcast publishes a message to all localized peers using gossipsub.
|
||||
// msg must be a proto.Message that can be encoded into a byte array.
|
||||
// It publishes the first 100 chars of msg over the msg's mapped topic.
|
||||
// Broadcast publishes a message to all localized peers using gossipsub.
|
||||
// msg must be a proto.Message that can be encoded into a byte array.
|
||||
// It publishes the first 100 chars of msg over the msg's mapped topic.
|
||||
// To map a messageType to a topic, use RegisterTopic.
|
||||
//
|
||||
// It logs an error if msg is not a protobuf message,
|
||||
// if msg cannot be encoded into a byte array,
|
||||
// or if the server is unable to publish the message over gossipsub.
|
||||
// It logs an error if msg is not a protobuf message,
|
||||
// if msg cannot be encoded into a byte array,
|
||||
// or if the server is unable to publish the message over gossipsub.
|
||||
//
|
||||
// msg := make(chan p2p.Message, 100) // Choose a reasonable buffer size!
|
||||
// ps.RegisterTopic("message_topic_here", msg)
|
||||
|
||||
@@ -67,13 +67,12 @@ type BeaconChainConfig struct {
|
||||
MaxCasperSlashings uint64 // MaxCasperSlashings defines the maximum number of casper FFG slashings possible in a block.
|
||||
|
||||
// Prysm constants.
|
||||
DepositsForChainStart uint64 // DepositsForChainStart defines how many validator deposits needed to kick off beacon chain.
|
||||
POWContractMerkleTreeDepth uint64 // POWContractMerkleTreeDepth defines the depth of PoW contract merkle tree.
|
||||
SimulatedBlockRandao [32]byte // SimulatedBlockRandao is a RANDAO seed stubbed in simulated block for advancing local beacon chain.
|
||||
RandBytes uint64 // RandBytes is the number of bytes used as entropy to shuffle validators.
|
||||
SyncPollingInterval int64 // SyncPollingInterval queries network nodes for sync status.
|
||||
GenesisTime time.Time // GenesisTime used by the protocol.
|
||||
MaxNumLog2Validators uint64 // MaxNumLog2Validators is the Max number of validators in Log2 exists given total ETH supply.
|
||||
DepositsForChainStart uint64 // DepositsForChainStart defines how many validator deposits needed to kick off beacon chain.
|
||||
SimulatedBlockRandao [32]byte // SimulatedBlockRandao is a RANDAO seed stubbed in simulated block for advancing local beacon chain.
|
||||
RandBytes uint64 // RandBytes is the number of bytes used as entropy to shuffle validators.
|
||||
SyncPollingInterval int64 // SyncPollingInterval queries network nodes for sync status.
|
||||
GenesisTime time.Time // GenesisTime used by the protocol.
|
||||
MaxNumLog2Validators uint64 // MaxNumLog2Validators is the Max number of validators in Log2 exists given total ETH supply.
|
||||
}
|
||||
|
||||
// ShardChainConfig contains configs for node to participate in shard chains.
|
||||
|
||||
@@ -2,15 +2,21 @@ load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["merkle.go"],
|
||||
srcs = ["deposit_trie.go"],
|
||||
importpath = "github.com/prysmaticlabs/prysm/shared/trie",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = ["//shared/hashutil:go_default_library"],
|
||||
deps = [
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["merkle_test.go"],
|
||||
srcs = ["deposit_trie_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = ["//shared/hashutil:go_default_library"],
|
||||
deps = [
|
||||
"//shared/hashutil:go_default_library",
|
||||
"//shared/params:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
81
shared/trie/deposit_trie.go
Normal file
81
shared/trie/deposit_trie.go
Normal file
@@ -0,0 +1,81 @@
|
||||
// Package trie contains definitions for building a Merkle trie for validator deposits
|
||||
// as defined in the Ethereum Serenity specification, as well as utilities to generate
|
||||
// and verify Merkle proofs.
|
||||
package trie
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
// DepositTrie represents a Merkle trie tracking deposits on the ETH 1.0
|
||||
// PoW chain contract created in Vyper.
|
||||
type DepositTrie struct {
|
||||
depositCount uint64
|
||||
merkleMap map[uint64][32]byte
|
||||
}
|
||||
|
||||
// NewDepositTrie creates a new struct instance representing a Merkle trie for deposits
|
||||
// and tracking an initial deposit count of 0.
|
||||
func NewDepositTrie() *DepositTrie {
|
||||
return &DepositTrie{
|
||||
depositCount: 0,
|
||||
merkleMap: make(map[uint64][32]byte),
|
||||
}
|
||||
}
|
||||
|
||||
// UpdateDepositTrie updates the Merkle trie representing deposits on
|
||||
// the ETH 1.0 PoW chain contract.
|
||||
func (d *DepositTrie) UpdateDepositTrie(depositData []byte) {
|
||||
twoToPowerOfTreeDepth := 1 << params.BeaconConfig().DepositContractTreeDepth
|
||||
index := d.depositCount + uint64(twoToPowerOfTreeDepth)
|
||||
d.merkleMap[index] = hashutil.Hash(depositData)
|
||||
for i := uint64(0); i < params.BeaconConfig().DepositContractTreeDepth; i++ {
|
||||
index = index / 2
|
||||
left := d.merkleMap[index*2]
|
||||
right := d.merkleMap[index*2+1]
|
||||
d.merkleMap[index] = hashutil.Hash(append(left[:], right[:]...))
|
||||
}
|
||||
d.depositCount++
|
||||
}
|
||||
|
||||
// GenerateMerkleBranch for a value up to the root from a leaf in the trie.
|
||||
func (d *DepositTrie) GenerateMerkleBranch(index uint64) [][]byte {
|
||||
twoToPowerOfTreeDepth := 1 << params.BeaconConfig().DepositContractTreeDepth
|
||||
idx := index + uint64(twoToPowerOfTreeDepth)
|
||||
branch := make([][]byte, params.BeaconConfig().DepositContractTreeDepth)
|
||||
for i := uint64(0); i < params.BeaconConfig().DepositContractTreeDepth; i++ {
|
||||
if idx%2 == 1 {
|
||||
value := d.merkleMap[idx-1]
|
||||
branch[i] = value[:]
|
||||
} else {
|
||||
value := d.merkleMap[idx+1]
|
||||
branch[i] = value[:]
|
||||
}
|
||||
idx = idx / 2
|
||||
}
|
||||
return branch
|
||||
}
|
||||
|
||||
// Root returns the Merkle root of the calculated deposit trie.
|
||||
func (d *DepositTrie) Root() [32]byte {
|
||||
return d.merkleMap[1]
|
||||
}
|
||||
|
||||
// VerifyMerkleBranch verifies a Merkle path in a trie
|
||||
// by checking the aggregated hash of contiguous leaves along a path
|
||||
// eventually equals the root hash of the Merkle trie.
|
||||
func VerifyMerkleBranch(leaf [32]byte, branch [][]byte, depth uint64, index uint64, root [32]byte) bool {
|
||||
twoToPowerOfTreeDepth := 1 << params.BeaconConfig().DepositContractTreeDepth
|
||||
idx := index + uint64(twoToPowerOfTreeDepth)
|
||||
value := leaf
|
||||
for i := uint64(0); i < depth; i++ {
|
||||
if idx%2 == 1 {
|
||||
value = hashutil.Hash(append(branch[i], value[:]...))
|
||||
} else {
|
||||
value = hashutil.Hash(append(value[:], branch[i]...))
|
||||
}
|
||||
idx = idx / 2
|
||||
}
|
||||
return value == root
|
||||
}
|
||||
65
shared/trie/deposit_trie_test.go
Normal file
65
shared/trie/deposit_trie_test.go
Normal file
@@ -0,0 +1,65 @@
|
||||
package trie
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
"github.com/prysmaticlabs/prysm/shared/params"
|
||||
)
|
||||
|
||||
func TestDepositTrie_UpdateDepositTrie(t *testing.T) {
|
||||
tests := []struct {
|
||||
deposits [][]byte
|
||||
}{
|
||||
{
|
||||
[][]byte{
|
||||
{1, 2, 3, 4},
|
||||
{5, 6, 7, 8},
|
||||
},
|
||||
},
|
||||
{
|
||||
[][]byte{
|
||||
{0, 0, 0, 0},
|
||||
{0, 0, 0, 0},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
d := NewDepositTrie()
|
||||
d.UpdateDepositTrie(tt.deposits[0])
|
||||
if d.depositCount != 1 {
|
||||
t.Errorf("Expected deposit count to increase by 1, received %d", d.depositCount)
|
||||
}
|
||||
d.UpdateDepositTrie(tt.deposits[1])
|
||||
if d.depositCount != 2 {
|
||||
t.Errorf("Expected deposit count to increase by 1, received %d", d.depositCount)
|
||||
}
|
||||
hash := hashutil.Hash(tt.deposits[1])
|
||||
twoToPowerOfTreeDepth := 1 << params.BeaconConfig().DepositContractTreeDepth
|
||||
lastLeaf := d.merkleMap[d.depositCount-1+uint64(twoToPowerOfTreeDepth)]
|
||||
if lastLeaf != hash {
|
||||
t.Errorf("Expected last leaf to equal %#x, received %#x", lastLeaf, hash)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDepositTrie_GenerateMerkleBranch(t *testing.T) {
|
||||
d := NewDepositTrie()
|
||||
deposit1 := []byte{1, 2, 3}
|
||||
d.UpdateDepositTrie(deposit1)
|
||||
deposit2 := []byte{5, 6, 7}
|
||||
d.UpdateDepositTrie(deposit2)
|
||||
deposit3 := []byte{8, 9, 10}
|
||||
d.UpdateDepositTrie(deposit3)
|
||||
index := d.depositCount - 1
|
||||
branch := d.GenerateMerkleBranch(index)
|
||||
if ok := VerifyMerkleBranch(
|
||||
hashutil.Hash(deposit3),
|
||||
branch,
|
||||
params.BeaconConfig().DepositContractTreeDepth,
|
||||
index,
|
||||
d.Root(),
|
||||
); !ok {
|
||||
t.Error("Expected Merkle branch to verify, received false")
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
package trie
|
||||
|
||||
import (
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
)
|
||||
|
||||
// VerifyMerkleBranch verifies a merkle path in a trie
|
||||
// by checking the aggregated hash of contiguous leaves along a path
|
||||
// eventually equals the root hash of the merkle trie.
|
||||
func VerifyMerkleBranch(leaf [32]byte, branch [][]byte, depth uint64, root [32]byte) bool {
|
||||
value := leaf
|
||||
for i := uint64(0); i < depth; i++ {
|
||||
if i%2 == 0 {
|
||||
value = hashutil.Hash(append(branch[i], value[:]...))
|
||||
} else {
|
||||
value = hashutil.Hash(append(value[:], branch[i]...))
|
||||
}
|
||||
}
|
||||
return value == root
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
package trie
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/prysmaticlabs/prysm/shared/hashutil"
|
||||
)
|
||||
|
||||
func TestVerifyMerkleBranch(t *testing.T) {
|
||||
// We build up a Merkle proof and then run
|
||||
// the verify function for data integrity testing
|
||||
// along a Merkle branch in a Merkle trie structure.
|
||||
depth := 4
|
||||
leaf := [32]byte{1, 2, 3}
|
||||
root := leaf
|
||||
branch := [][]byte{
|
||||
{4, 5, 6},
|
||||
{7, 8, 9},
|
||||
{10, 11, 12},
|
||||
{13, 14, 15},
|
||||
}
|
||||
for i := 0; i < depth; i++ {
|
||||
if i%2 == 0 {
|
||||
root = hashutil.Hash(append(branch[i], root[:]...))
|
||||
} else {
|
||||
root = hashutil.Hash(append(root[:], branch[i]...))
|
||||
}
|
||||
}
|
||||
if ok := VerifyMerkleBranch(leaf, branch, uint64(depth), root); !ok {
|
||||
t.Error("Expected merkle branch to verify, received false")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user