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

ACP-77: Implement RegisterSubnetValidatorTx #3420

Draft
wants to merge 46 commits into
base: implement-acp-77-update-convert-subnet-tx
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
ae9ec6f
wip
StephenButtolph Sep 24, 2024
584b4aa
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Sep 25, 2024
8905432
implement execution logic
StephenButtolph Sep 26, 2024
2618db2
remove duplicate public key
StephenButtolph Sep 26, 2024
322642b
finish e2e RegisterSubnetValidatorTx
StephenButtolph Sep 26, 2024
d68d935
improve error + logs
StephenButtolph Sep 26, 2024
dd41b73
improve doc
StephenButtolph Sep 26, 2024
ac054c7
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 1, 2024
01afe2a
Fix compilation
StephenButtolph Oct 1, 2024
38e8102
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 1, 2024
92f2af9
Update registerSubnetValdiatorTx
StephenButtolph Oct 1, 2024
fa12221
Fix wallet
StephenButtolph Oct 2, 2024
be028f8
merged
StephenButtolph Oct 2, 2024
755ebcb
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 2, 2024
c80e103
nit
StephenButtolph Oct 2, 2024
f1b3b85
Merge branch 'implement-acp-77-register-subnet-validator-tx' of githu…
StephenButtolph Oct 2, 2024
bfb226f
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 2, 2024
9025604
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 2, 2024
efb96b1
merged
StephenButtolph Oct 3, 2024
a25618e
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 3, 2024
80a7cbe
fix lint
StephenButtolph Oct 4, 2024
9ef28a1
revert test change
StephenButtolph Oct 4, 2024
59de74a
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 7, 2024
dda2598
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 7, 2024
2373cf6
nit
StephenButtolph Oct 7, 2024
4ba542a
wip e2e test
StephenButtolph Oct 8, 2024
1936cfc
wip
StephenButtolph Oct 8, 2024
f92b9c5
wip
StephenButtolph Oct 8, 2024
5875418
nit
StephenButtolph Oct 8, 2024
b0c0f0f
fix error message
StephenButtolph Oct 8, 2024
5e7be2b
fix log
StephenButtolph Oct 8, 2024
5ddff5e
merged
StephenButtolph Oct 8, 2024
afeabb6
backport tests
StephenButtolph Oct 8, 2024
9dc7447
merged
StephenButtolph Oct 8, 2024
fb4d8e9
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 8, 2024
ffd6a3a
merge
StephenButtolph Oct 8, 2024
5c86e5c
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 8, 2024
1815f64
Fix diff
StephenButtolph Oct 8, 2024
0dd0041
reduce diff
StephenButtolph Oct 8, 2024
1f168c7
Add RegisterSubnetValidatorTx complexity tests
StephenButtolph Oct 8, 2024
fcdce8c
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 8, 2024
1d4d0fe
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 10, 2024
c778c78
merged
StephenButtolph Oct 11, 2024
e1e04de
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 14, 2024
96625ae
Fix merge
StephenButtolph Oct 14, 2024
f1518a8
Merge branch 'implement-acp-77-update-convert-subnet-tx' into impleme…
StephenButtolph Oct 15, 2024
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
247 changes: 245 additions & 2 deletions tests/e2e/p/permissionless_layer_one.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,53 @@
package p

import (
"context"
"errors"
"math"
"slices"
"time"

"github.com/onsi/ginkgo/v2"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"

"github.com/ava-labs/avalanchego/api/info"
"github.com/ava-labs/avalanchego/config"
"github.com/ava-labs/avalanchego/ids"
"github.com/ava-labs/avalanchego/network/peer"
"github.com/ava-labs/avalanchego/proto/pb/sdk"
"github.com/ava-labs/avalanchego/snow/networking/router"
"github.com/ava-labs/avalanchego/tests/fixture/e2e"
"github.com/ava-labs/avalanchego/tests/fixture/tmpnet"
"github.com/ava-labs/avalanchego/utils"
"github.com/ava-labs/avalanchego/utils/buffer"
"github.com/ava-labs/avalanchego/utils/constants"
"github.com/ava-labs/avalanchego/utils/crypto/bls"
"github.com/ava-labs/avalanchego/utils/crypto/secp256k1"
"github.com/ava-labs/avalanchego/utils/logging"
"github.com/ava-labs/avalanchego/utils/set"
"github.com/ava-labs/avalanchego/utils/units"
"github.com/ava-labs/avalanchego/vms/example/xsvm/genesis"
"github.com/ava-labs/avalanchego/vms/platformvm/txs"
"github.com/ava-labs/avalanchego/vms/platformvm/warp"
"github.com/ava-labs/avalanchego/vms/platformvm/warp/payload"
"github.com/ava-labs/avalanchego/vms/secp256k1fx"

p2pmessage "github.com/ava-labs/avalanchego/message"
p2psdk "github.com/ava-labs/avalanchego/network/p2p"
p2ppb "github.com/ava-labs/avalanchego/proto/pb/p2p"
snowvalidators "github.com/ava-labs/avalanchego/snow/validators"
platformvmsdk "github.com/ava-labs/avalanchego/vms/platformvm"
platformvmvalidators "github.com/ava-labs/avalanchego/vms/platformvm/validators"
warpmessage "github.com/ava-labs/avalanchego/vms/platformvm/warp/message"
)

const (
genesisWeight = units.Schmeckle
genesisBalance = units.Avax
genesisWeight = units.Schmeckle
genesisBalance = units.Avax
registerWeight = genesisWeight / 10
registerBalance = 0
)

var _ = e2e.DescribePChain("[Permissionless L1]", func() {
Expand Down Expand Up @@ -135,6 +155,27 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() {
genesisNodePK, err := bls.PublicKeyFromCompressedBytes(genesisNodePoP.PublicKey[:])
require.NoError(err)

tc.By("connecting to the genesis validator")
var (
networkID = env.GetNetwork().GetNetworkID()
genesisPeerMessages = buffer.NewUnboundedBlockingDeque[p2pmessage.InboundMessage](1)
)
genesisPeer, err := peer.StartTestPeer(
tc.DefaultContext(),
subnetGenesisNode.StakingAddress,
networkID,
router.InboundHandlerFunc(func(_ context.Context, m p2pmessage.InboundMessage) {
tc.Outf("received %s %s from %s\n", m.Op(), m.Message(), m.NodeID())
genesisPeerMessages.PushRight(m)
}),
)
require.NoError(err)
defer func() {
genesisPeerMessages.Close()
genesisPeer.StartClose()
require.NoError(genesisPeer.AwaitClosed(tc.DefaultContext()))
}()

address := []byte{}
tc.By("issuing a ConvertSubnetTx", func() {
_, err := pWallet.IssueConvertSubnetTx(
Expand Down Expand Up @@ -205,5 +246,207 @@ var _ = e2e.DescribePChain("[Permissionless L1]", func() {
})
})
})

advanceProposerVMPChainHeight := func() {
// We first must wait at least [RecentlyAcceptedWindowTTL] to ensure
// the next block will evict the prior block from the windower.
time.Sleep((5 * platformvmvalidators.RecentlyAcceptedWindowTTL) / 4)

// Now we must:
// 1. issue a block which should include the old P-chain height.
// 2. issue a block which should include the new P-chain height.
for range 2 {
_, err = pWallet.IssueBaseTx(nil, tc.WithDefaultContext())
require.NoError(err)
}
// Now that a block has been issued with the new P-chain height, the
// next block will use that height for warp message verification.
}
tc.By("advancing the proposervm P-chain height", advanceProposerVMPChainHeight)

tc.By("creating the validator to register")
subnetRegisterNode := e2e.AddEphemeralNode(tc, env.GetNetwork(), tmpnet.FlagsMap{
config.TrackSubnetsKey: subnetID.String(),
})

registerNodePoP, err := subnetRegisterNode.GetProofOfPossession()
require.NoError(err)

tc.By("ensuring the subnet nodes are healthy", func() {
e2e.WaitForHealthy(tc, subnetGenesisNode)
e2e.WaitForHealthy(tc, subnetRegisterNode)
})

tc.By("creating the RegisterSubnetValidatorMessage")
registerSubnetValidatorMessage, err := warpmessage.NewRegisterSubnetValidator(
subnetID,
subnetRegisterNode.NodeID,
registerNodePoP.PublicKey,
uint64(time.Now().Add(5*time.Minute).Unix()),
warpmessage.PChainOwner{},
warpmessage.PChainOwner{},
registerWeight,
)
require.NoError(err)

tc.By("registering the validator", func() {
tc.By("creating the unsigned warp message")
unsignedRegisterSubnetValidator := must[*warp.UnsignedMessage](tc)(warp.NewUnsignedMessage(
networkID,
chainID,
must[*payload.AddressedCall](tc)(payload.NewAddressedCall(
address,
registerSubnetValidatorMessage.Bytes(),
)).Bytes(),
))

tc.By("sending the request to sign the warp message", func() {
registerSubnetValidatorRequest, err := wrapWarpSignatureRequest(
unsignedRegisterSubnetValidator,
nil,
)
require.NoError(err)

require.True(genesisPeer.Send(tc.DefaultContext(), registerSubnetValidatorRequest))
})

tc.By("getting the signature response")
registerSubnetValidatorSignature, ok, err := findMessage(genesisPeerMessages, unwrapWarpSignature)
require.NoError(err)
require.True(ok)

tc.By("creating the signed warp message to register the validator")
signers := set.NewBits()
signers.Add(0) // [signers] has weight from the genesis peer

var sigBytes [bls.SignatureLen]byte
copy(sigBytes[:], bls.SignatureToBytes(registerSubnetValidatorSignature))
registerSubnetValidator, err := warp.NewMessage(
unsignedRegisterSubnetValidator,
&warp.BitSetSignature{
Signers: signers.Bytes(),
Signature: sigBytes,
},
)
require.NoError(err)

tc.By("issuing a RegisterSubnetValidatorTx", func() {
_, err := pWallet.IssueRegisterSubnetValidatorTx(
registerBalance,
registerNodePoP.ProofOfPossession,
registerSubnetValidator.Bytes(),
)
require.NoError(err)
})
})

tc.By("verifying the validator was registered", func() {
tc.By("verifying the validator set was updated", func() {
verifyValidatorSet(map[ids.NodeID]*snowvalidators.GetValidatorOutput{
subnetGenesisNode.NodeID: {
NodeID: subnetGenesisNode.NodeID,
PublicKey: genesisNodePK,
Weight: genesisWeight,
},
ids.EmptyNodeID: { // The validator is not active
NodeID: ids.EmptyNodeID,
Weight: registerWeight,
},
})
})
})
})
})

func wrapWarpSignatureRequest(
msg *warp.UnsignedMessage,
justification []byte,
) (p2pmessage.OutboundMessage, error) {
p2pMessageFactory, err := p2pmessage.NewCreator(
logging.NoLog{},
prometheus.NewRegistry(),
constants.DefaultNetworkCompressionType,
10*time.Second,
)
if err != nil {
return nil, err
}

request := sdk.SignatureRequest{
Message: msg.Bytes(),
Justification: justification,
}
requestBytes, err := proto.Marshal(&request)
if err != nil {
return nil, err
}

return p2pMessageFactory.AppRequest(
msg.SourceChainID,
0,
time.Hour,
p2psdk.PrefixMessage(
p2psdk.ProtocolPrefix(p2psdk.SignatureRequestHandlerID),
requestBytes,
),
)
}

func findMessage[T any](
q buffer.BlockingDeque[p2pmessage.InboundMessage],
parser func(p2pmessage.InboundMessage) (T, bool, error),
) (T, bool, error) {
var messagesToReprocess []p2pmessage.InboundMessage
defer func() {
slices.Reverse(messagesToReprocess)
for _, msg := range messagesToReprocess {
q.PushLeft(msg)
}
}()

for {
msg, ok := q.PopLeft()
if !ok {
return utils.Zero[T](), false, nil
}

parsed, ok, err := parser(msg)
if err != nil {
return utils.Zero[T](), false, err
}
if ok {
return parsed, true, nil
}

messagesToReprocess = append(messagesToReprocess, msg)
}
}

// unwrapWarpSignature assumes the only type of AppResponses that will be
// received are ACP-118 compliant responses.
func unwrapWarpSignature(msg p2pmessage.InboundMessage) (*bls.Signature, bool, error) {
var appResponse *p2ppb.AppResponse
switch msg := msg.Message().(type) {
case *p2ppb.AppResponse:
appResponse = msg
case *p2ppb.AppError:
return nil, false, errors.New(msg.ErrorMessage)
default:
return nil, false, nil
}

var response sdk.SignatureResponse
if err := proto.Unmarshal(appResponse.AppBytes, &response); err != nil {
return nil, false, err
}

warpSignature, err := bls.SignatureFromBytes(response.Signature)
return warpSignature, true, err
}

func must[T any](t require.TestingT) func(T, error) T {
return func(val T, err error) T {
require.NoError(t, err)
return val
}
}
11 changes: 9 additions & 2 deletions vms/platformvm/metrics/tx_metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,16 +132,23 @@ func (m *txMetrics) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) er
return nil
}

func (m *txMetrics) BaseTx(*txs.BaseTx) error {
m.numTxs.With(prometheus.Labels{
txLabel: "base",
}).Inc()
return nil
}

func (m *txMetrics) ConvertSubnetTx(*txs.ConvertSubnetTx) error {
m.numTxs.With(prometheus.Labels{
txLabel: "convert_subnet",
}).Inc()
return nil
}

func (m *txMetrics) BaseTx(*txs.BaseTx) error {
func (m *txMetrics) RegisterSubnetValidatorTx(*txs.RegisterSubnetValidatorTx) error {
m.numTxs.With(prometheus.Labels{
txLabel: "base",
txLabel: "register_subnet_validator",
}).Inc()
return nil
}
1 change: 1 addition & 0 deletions vms/platformvm/txs/codec.go
Original file line number Diff line number Diff line change
Expand Up @@ -123,5 +123,6 @@ func RegisterDurangoTypes(targetCodec linearcodec.Codec) error {
func RegisterEtnaTypes(targetCodec linearcodec.Codec) error {
return errors.Join(
targetCodec.RegisterType(&ConvertSubnetTx{}),
targetCodec.RegisterType(&RegisterSubnetValidatorTx{}),
)
}
10 changes: 7 additions & 3 deletions vms/platformvm/txs/executor/atomic_tx_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@ func (*AtomicTxExecutor) TransformSubnetTx(*txs.TransformSubnetTx) error {
return ErrWrongTxType
}

func (*AtomicTxExecutor) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) error {
func (*AtomicTxExecutor) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error {
return ErrWrongTxType
}

func (*AtomicTxExecutor) AddPermissionlessValidatorTx(*txs.AddPermissionlessValidatorTx) error {
func (*AtomicTxExecutor) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDelegatorTx) error {
return ErrWrongTxType
}

func (*AtomicTxExecutor) AddPermissionlessDelegatorTx(*txs.AddPermissionlessDelegatorTx) error {
func (*AtomicTxExecutor) TransferSubnetOwnershipTx(*txs.TransferSubnetOwnershipTx) error {
return ErrWrongTxType
}

Expand All @@ -86,6 +86,10 @@ func (*AtomicTxExecutor) ConvertSubnetTx(*txs.ConvertSubnetTx) error {
return ErrWrongTxType
}

func (*AtomicTxExecutor) RegisterSubnetValidatorTx(*txs.RegisterSubnetValidatorTx) error {
return ErrWrongTxType
}

func (e *AtomicTxExecutor) ImportTx(tx *txs.ImportTx) error {
return e.atomicTx(tx)
}
Expand Down
4 changes: 4 additions & 0 deletions vms/platformvm/txs/executor/proposal_tx_executor.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@ func (*ProposalTxExecutor) ConvertSubnetTx(*txs.ConvertSubnetTx) error {
return ErrWrongTxType
}

func (*ProposalTxExecutor) RegisterSubnetValidatorTx(*txs.RegisterSubnetValidatorTx) error {
return ErrWrongTxType
}

func (e *ProposalTxExecutor) AddValidatorTx(tx *txs.AddValidatorTx) error {
// AddValidatorTx is a proposal transaction until the Banff fork
// activation. Following the activation, AddValidatorTxs must be issued into
Expand Down
Loading
Loading