Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove Transfers #3870

Merged
merged 7 commits into from
Oct 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion beacon-chain/core/blocks/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ go_test(
"//proto/eth/v1alpha1:go_default_library",
"//shared/bls:go_default_library",
"//shared/bytesutil:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/params:go_default_library",
"//shared/testutil:go_default_library",
"//shared/trieutil:go_default_library",
Expand Down
124 changes: 0 additions & 124 deletions beacon-chain/core/blocks/block_operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -1121,130 +1121,6 @@ func VerifyExit(beaconState *pb.BeaconState, exit *ethpb.VoluntaryExit) error {
return nil
}

// ProcessTransfers is one of the operations performed
// on each processed beacon block to determine transfers between beacon chain balances.
//
// Spec pseudocode definition:
// def process_transfer(state: BeaconState, transfer: Transfer) -> None:
// """
// Process ``Transfer`` operation.
// """
// # Verify the balance the covers amount and fee (with overflow protection)
// assert state.balances[transfer.sender] >= max(transfer.amount + transfer.fee, transfer.amount, transfer.fee)
// # A transfer is valid in only one slot
// assert state.slot == transfer.slot
// # SenderIndex must satisfy at least one of the following conditions in the parenthesis:
// assert (
// # * Has not been activated
// state.validator_registry[transfer.sender].activation_eligibility_epoch == FAR_FUTURE_EPOCH or
// # * Is withdrawable
// get_current_epoch(state) >= state.validator_registry[transfer.sender].withdrawable_epoch or
// # * Balance after transfer is more than the effective balance threshold
// transfer.amount + transfer.fee + MAX_EFFECTIVE_BALANCE <= state.balances[transfer.sender]
// )
// # Verify that the pubkey is valid
// assert (
// state.validator_registry[transfer.sender].withdrawal_credentials ==
// int_to_bytes(BLS_WITHDRAWAL_PREFIX, length=1) + hash(transfer.pubkey)[1:]
// )
// # Verify that the signature is valid
// assert bls_verify(transfer.pubkey, signing_root(transfer), transfer.signature, get_domain(state, DOMAIN_TRANSFER))
// # Process the transfer
// decrease_balance(state, transfer.sender, transfer.amount + transfer.fee)
// increase_balance(state, transfer.recipient, transfer.amount)
// increase_balance(state, get_beacon_proposer_index(state), transfer.fee)
// # Verify balances are not dust
// assert not (0 < state.balances[transfer.sender] < MIN_DEPOSIT_AMOUNT)
// assert not (0 < state.balances[transfer.recipient] < MIN_DEPOSIT_AMOUNT)
func ProcessTransfers(
beaconState *pb.BeaconState,
body *ethpb.BeaconBlockBody,
) (*pb.BeaconState, error) {
transfers := body.Transfers

for idx, transfer := range transfers {
if err := verifyTransfer(beaconState, transfer); err != nil {
return nil, errors.Wrapf(err, "could not verify transfer %d", idx)
}
// Process the transfer between accounts.
beaconState = helpers.DecreaseBalance(beaconState, transfer.SenderIndex, transfer.Amount+transfer.Fee)
beaconState = helpers.IncreaseBalance(beaconState, transfer.RecipientIndex, transfer.Amount)
proposerIndex, err := helpers.BeaconProposerIndex(beaconState)
if err != nil {
return nil, errors.Wrap(err, "could not determine beacon proposer index")
}
beaconState = helpers.IncreaseBalance(beaconState, proposerIndex, transfer.Fee)

// Finally, we verify balances will not go below the mininum.
if beaconState.Balances[transfer.SenderIndex] < params.BeaconConfig().MinDepositAmount &&
0 < beaconState.Balances[transfer.SenderIndex] {
return nil, fmt.Errorf(
"sender balance below critical level: %v",
beaconState.Balances[transfer.SenderIndex],
)
}
if beaconState.Balances[transfer.RecipientIndex] < params.BeaconConfig().MinDepositAmount &&
0 < beaconState.Balances[transfer.RecipientIndex] {
return nil, fmt.Errorf(
"recipient balance below critical level: %v",
beaconState.Balances[transfer.RecipientIndex],
)
}
}
return beaconState, nil
}

func verifyTransfer(beaconState *pb.BeaconState, transfer *ethpb.Transfer) error {
if transfer.SenderIndex > uint64(len(beaconState.Validators)) {
return errors.New("transfer sender index out of bounds in validator registry")
}

maxVal := transfer.Fee
if transfer.Amount > maxVal {
maxVal = transfer.Amount
}
if transfer.Amount+transfer.Fee > maxVal {
maxVal = transfer.Amount + transfer.Fee
}
sender := beaconState.Validators[transfer.SenderIndex]
senderBalance := beaconState.Balances[transfer.SenderIndex]
// Verify the balance the covers amount and fee (with overflow protection).
if senderBalance < maxVal {
return fmt.Errorf("expected sender balance %d >= %d", senderBalance, maxVal)
}
// A transfer is valid in only one slot.
if beaconState.Slot != transfer.Slot {
return fmt.Errorf("expected beacon state slot %d == transfer slot %d", beaconState.Slot, transfer.Slot)
}

// Sender must be not yet eligible for activation, withdrawn, or transfer balance over MAX_EFFECTIVE_BALANCE.
senderNotActivationEligible := sender.ActivationEligibilityEpoch == params.BeaconConfig().FarFutureEpoch
senderNotWithdrawn := helpers.CurrentEpoch(beaconState) >= sender.WithdrawableEpoch
underMaxTransfer := transfer.Amount+transfer.Fee+params.BeaconConfig().MaxEffectiveBalance <= senderBalance

if !(senderNotActivationEligible || senderNotWithdrawn || underMaxTransfer) {
return fmt.Errorf(
"expected activation eligiblity: false or withdrawn: false or over max transfer: false, received %v %v %v",
senderNotActivationEligible,
senderNotWithdrawn,
underMaxTransfer,
)
}
// Verify that the pubkey is valid.
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
hashed := hashutil.Hash(transfer.SenderWithdrawalPublicKey)
buf = append(buf, hashed[:][1:]...)
if !bytes.Equal(sender.WithdrawalCredentials, buf) {
return fmt.Errorf("invalid public key, expected %v, received %v", buf, sender.WithdrawalCredentials)
}

domain := helpers.Domain(beaconState.Fork, helpers.CurrentEpoch(beaconState), params.BeaconConfig().DomainTransfer)
if err := verifySigningRoot(transfer, transfer.SenderWithdrawalPublicKey, transfer.Signature, domain); err != nil {
return errors.Wrap(err, "could not verify transfer signature")
}
return nil
}

// ClearEth1DataVoteCache clears the eth1 data vote count cache.
func ClearEth1DataVoteCache() {
eth1DataCache = cache.NewEth1DataVoteCache()
Expand Down
201 changes: 0 additions & 201 deletions beacon-chain/core/blocks/block_operations_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ import (
ethpb "github.com/prysmaticlabs/prysm/proto/eth/v1alpha1"
"github.com/prysmaticlabs/prysm/shared/bls"
"github.com/prysmaticlabs/prysm/shared/bytesutil"
"github.com/prysmaticlabs/prysm/shared/hashutil"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/testutil"
"github.com/prysmaticlabs/prysm/shared/trieutil"
Expand Down Expand Up @@ -2102,203 +2101,3 @@ func TestProcessVoluntaryExits_AppliesCorrectStatus(t *testing.T) {
helpers.DelayedActivationExitEpoch(state.Slot/params.BeaconConfig().SlotsPerEpoch), newRegistry[0].ExitEpoch)
}
}

func TestProcessBeaconTransfers_NotEnoughSenderIndexBalance(t *testing.T) {
registry := []*ethpb.Validator{
{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
state := &pb.BeaconState{
Validators: registry,
Balances: balances,
}
transfers := []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MaxEffectiveBalance,
Amount: params.BeaconConfig().MaxEffectiveBalance,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: transfers,
},
}
want := fmt.Sprintf(
"expected sender balance %d >= %d",
balances[0],
transfers[0].Fee+transfers[0].Amount,
)
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}

func TestProcessBeaconTransfers_FailsVerification(t *testing.T) {
testConfig := params.BeaconConfig()
testConfig.MaxTransfers = 1
params.OverrideBeaconConfig(testConfig)
registry := []*ethpb.Validator{
{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
},
{
ActivationEligibilityEpoch: params.BeaconConfig().FarFutureEpoch,
},
}
balances := []uint64{params.BeaconConfig().MaxEffectiveBalance}
state := &pb.BeaconState{
Slot: 0,
Validators: registry,
Balances: balances,
}
transfers := []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MaxEffectiveBalance + 1,
},
}
block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: transfers,
},
}
want := fmt.Sprintf(
"expected sender balance %d >= %d",
balances[0],
transfers[0].Fee,
)
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}

block.Body.Transfers = []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MinDepositAmount,
Slot: state.Slot + 1,
},
}
want = fmt.Sprintf(
"expected beacon state slot %d == transfer slot %d",
state.Slot,
block.Body.Transfers[0].Slot,
)
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}

state.Validators[0].WithdrawableEpoch = params.BeaconConfig().FarFutureEpoch
state.Validators[0].ActivationEligibilityEpoch = 0
state.Balances[0] = params.BeaconConfig().MinDepositAmount + params.BeaconConfig().MaxEffectiveBalance
block.Body.Transfers = []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MaxEffectiveBalance,
Slot: state.Slot,
},
}
want = "over max transfer"
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}

state.Validators[0].WithdrawableEpoch = 0
state.Validators[0].ActivationEligibilityEpoch = params.BeaconConfig().FarFutureEpoch
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
pubKey := []byte("B")
hashed := hashutil.Hash(pubKey)
buf = append(buf, hashed[:]...)
state.Validators[0].WithdrawalCredentials = buf
block.Body.Transfers = []*ethpb.Transfer{
{
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MinDepositAmount,
Slot: state.Slot,
SenderWithdrawalPublicKey: []byte("A"),
},
}
want = "invalid public key"
if _, err := blocks.ProcessTransfers(state, block.Body); !strings.Contains(err.Error(), want) {
t.Errorf("Expected %s, received %v", want, err)
}
}

func TestProcessBeaconTransfers_OK(t *testing.T) {
helpers.ClearShuffledValidatorCache()
testConfig := params.BeaconConfig()
testConfig.MaxTransfers = 1
params.OverrideBeaconConfig(testConfig)
validators := make([]*ethpb.Validator, params.BeaconConfig().MinGenesisActiveValidatorCount/32)
for i := 0; i < len(validators); i++ {
validators[i] = &ethpb.Validator{
ActivationEpoch: 0,
ExitEpoch: params.BeaconConfig().FarFutureEpoch,
Slashed: false,
WithdrawableEpoch: 0,
}
}
validatorBalances := make([]uint64, len(validators))
for i := 0; i < len(validatorBalances); i++ {
validatorBalances[i] = params.BeaconConfig().MaxEffectiveBalance
}

state := &pb.BeaconState{
Validators: validators,
Slot: 0,
Balances: validatorBalances,
Fork: &pb.Fork{
CurrentVersion: params.BeaconConfig().GenesisForkVersion,
PreviousVersion: params.BeaconConfig().GenesisForkVersion,
},
RandaoMixes: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
Slashings: make([]uint64, params.BeaconConfig().EpochsPerSlashingsVector),
ActiveIndexRoots: make([][]byte, params.BeaconConfig().EpochsPerHistoricalVector),
}

transfer := &ethpb.Transfer{
SenderIndex: 0,
RecipientIndex: 1,
Fee: params.BeaconConfig().MinDepositAmount,
Amount: params.BeaconConfig().MinDepositAmount,
Slot: state.Slot,
}

priv, err := bls.RandKey(rand.Reader)
if err != nil {
t.Fatal(err)
}
pubKey := priv.PublicKey().Marshal()[:]
transfer.SenderWithdrawalPublicKey = pubKey
state.Validators[transfer.SenderIndex].PublicKey = pubKey
signingRoot, err := ssz.SigningRoot(transfer)
if err != nil {
t.Fatalf("Failed to get signing root of block: %v", err)
}
epoch := helpers.CurrentEpoch(state)
dt := helpers.Domain(state.Fork, epoch, params.BeaconConfig().DomainTransfer)
transferSig := priv.Sign(signingRoot[:], dt)
transfer.Signature = transferSig.Marshal()[:]

block := &ethpb.BeaconBlock{
Body: &ethpb.BeaconBlockBody{
Transfers: []*ethpb.Transfer{transfer},
},
}
buf := []byte{params.BeaconConfig().BLSWithdrawalPrefixByte}
hashed := hashutil.Hash(pubKey)
buf = append(buf, hashed[:][1:]...)
state.Validators[0].WithdrawalCredentials = buf
state.Validators[0].ActivationEligibilityEpoch = params.BeaconConfig().FarFutureEpoch
newState, err := blocks.ProcessTransfers(state, block.Body)
if err != nil {
t.Errorf("Unexpected error: %v", err)
}
expectedRecipientIndex := params.BeaconConfig().MaxEffectiveBalance + block.Body.Transfers[0].Amount
if newState.Balances[1] != expectedRecipientIndex {
t.Errorf("Expected recipient balance %d, received %d", newState.Balances[1], expectedRecipientIndex)
}
expectedSenderIndex := params.BeaconConfig().MaxEffectiveBalance - block.Body.Transfers[0].Amount - block.Body.Transfers[0].Fee
if newState.Balances[0] != expectedSenderIndex {
t.Errorf("Expected sender balance %d, received %d", newState.Balances[0], expectedSenderIndex)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import (
)

func TestBlockHeaderMainnet(t *testing.T) {
t.Skip("Disabled until v0.9.0 (#3865) completes")
runBlockHeaderTest(t, "mainnet")
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,7 @@ import (
)

func TestBlockHeaderMinimal(t *testing.T) {
t.Skip("Disabled until v0.9.0 (#3865) completes")

runBlockHeaderTest(t, "minimal")
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import (
)

func TestBlockProcessingMainnetYaml(t *testing.T) {
t.Skip("Disabled until v0.9.0 (#3865) completes")
runBlockProcessingTest(t, "mainnet")
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import (
)

func TestBlockProcessingMinimalYaml(t *testing.T) {
t.Skip("Disabled until v0.9.0 (#3865) completes")
runBlockProcessingTest(t, "minimal")
}
1 change: 0 additions & 1 deletion beacon-chain/core/state/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ go_library(
"//proto/beacon/p2p/v1:go_default_library",
"//proto/eth/v1alpha1:go_default_library",
"//shared/featureconfig:go_default_library",
"//shared/hashutil:go_default_library",
"//shared/mathutil:go_default_library",
"//shared/params:go_default_library",
"//shared/traceutil:go_default_library",
Expand Down
2 changes: 2 additions & 0 deletions beacon-chain/core/state/minimal_config_consensus_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import (
)

func TestConsensusBugs(t *testing.T) {
t.Skip("Disabled until v0.9.0 (#3865) completes")

tests := []struct {
name string
blockPath string
Expand Down
Loading