Implement PoW Chain Deposit Trie in Go (#1285)

This commit is contained in:
Raul Jordan
2019-01-11 16:29:30 +08:00
committed by GitHub
parent dc5807b67d
commit 1f11b821ab
10 changed files with 195 additions and 107 deletions

View File

@@ -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",

View File

@@ -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(

View File

@@ -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),
}

View File

@@ -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)

View File

@@ -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.

View File

@@ -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",
],
)

View 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
}

View 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")
}
}

View File

@@ -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
}

View File

@@ -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")
}
}