-
Notifications
You must be signed in to change notification settings - Fork 2
ConvertSubnetToL1 P Chain Tx #153
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
base: make-sdk-uptodate
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,148 @@ | ||
| // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
| package main | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
| "os" | ||
|
|
||
| "github.com/ava-labs/avalanche-tooling-sdk-go/blockchain" | ||
| "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" | ||
| "github.com/ava-labs/avalanche-tooling-sdk-go/network" | ||
| "github.com/ava-labs/avalanche-tooling-sdk-go/transactions/p-chain/txs" | ||
| "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" | ||
| "github.com/ava-labs/avalanchego/ids" | ||
| "github.com/ava-labs/avalanchego/utils/formatting/address" | ||
| avagoTxs "github.com/ava-labs/avalanchego/vms/platformvm/txs" | ||
| "github.com/ava-labs/avalanchego/vms/platformvm/warp/message" | ||
| "github.com/ava-labs/avalanchego/wallet/subnet/primary" | ||
| "github.com/ethereum/go-ethereum/common" | ||
| ) | ||
|
|
||
| func ConvertL1() error { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you make this very similar to https://github.com/javiertc/avalanche-sdk-test/blob/main/convertSubnetToL1Tx.ts ? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this way you will check what is being used and the separate parts: chain, account, wallet, functions There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you can also try to refer to my example with is pretty much based on https://github.com/javiertc/avalanche-sdk-test/blob/main/createSubnetAndChain.ts There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. will refactor to be only usign wallet, need to create PR for that |
||
| // Configuration - Replace these with your actual values | ||
| const ( | ||
| // Your private key file path | ||
| privateKeyFilePath = "" | ||
|
|
||
| // Subnet and Chain IDs | ||
| subnetIDStr = "" | ||
| chainIDStr = "" | ||
|
|
||
| // Validator information | ||
| nodeIDStr = "" | ||
| BLSPublicKey = "0x..." // Replace with actual BLS public key | ||
| BLSProofOfPossession = "0x..." // Replace with actual BLS proof | ||
| ChangeOwnerAddr = "P-fujixxx" // Address to receive remaining balance | ||
| Weight = 100 // Validator weight | ||
| Balance = 1000000000 // Validator balance in nAVAX | ||
|
|
||
| // Validator manager contract address | ||
| validatorManagerAddr = "0x0FEEDC0DE0000000000000000000000000000000" // Replace with actual contract address | ||
| ) | ||
|
|
||
| // Subnet auth keys (addresses that can sign the conversion tx) | ||
| subnetAuthKeysStrs := []string{ | ||
| "P-fujixxx", // Replace with actual addresses | ||
| } | ||
|
|
||
| network := network.FujiNetwork() | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. can you take the network endpoint from env var as in my example There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why do we want to use env var as opposed to just declaring it in example? |
||
| keychain, err := keychain.NewKeychain(network, privateKeyFilePath, nil) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets create keychain from string private key in env var There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here switching to wallet |
||
| if err != nil { | ||
| return fmt.Errorf("failed to create keychain: %w", err) | ||
| } | ||
|
|
||
| // Parse IDs | ||
| subnetID, err := ids.FromString(subnetIDStr) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to parse subnet ID: %w", err) | ||
| } | ||
|
|
||
| chainID, err := ids.FromString(chainIDStr) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to parse chain ID: %w", err) | ||
| } | ||
|
|
||
| deployer := txs.NewPublicDeployer(keychain, network) | ||
|
|
||
| wallet, err := wallet.New( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. probably my new wallet interface is better as it does directly receives SDK objects |
||
| context.Background(), | ||
| network.Endpoint, | ||
| keychain.Keychain, | ||
| primary.WalletConfig{ | ||
| SubnetIDs: []ids.ID{subnetID}, | ||
| }, | ||
| ) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to create wallet: %w", err) | ||
| } | ||
|
|
||
| subnetAuthKeys, err := address.ParseToIDs(subnetAuthKeysStrs) | ||
| if err != nil { | ||
| return fmt.Errorf("failure parsing auth keys: %w", err) | ||
| } | ||
|
|
||
| bootstrapValidators := []*avagoTxs.ConvertSubnetToL1Validator{} | ||
| nodeID, err := ids.NodeIDFromString(nodeIDStr) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to parse node ID: %w", err) | ||
| } | ||
|
|
||
| blsInfo, err := blockchain.ConvertToBLSProofOfPossession(BLSPublicKey, BLSProofOfPossession) | ||
| if err != nil { | ||
| return fmt.Errorf("failure parsing BLS info: %w", err) | ||
| } | ||
|
|
||
| addrs, err := address.ParseToIDs([]string{ChangeOwnerAddr}) | ||
| if err != nil { | ||
| return fmt.Errorf("failure parsing change owner address: %w", err) | ||
| } | ||
|
|
||
| bootstrapValidator := &avagoTxs.ConvertSubnetToL1Validator{ | ||
| NodeID: nodeID[:], | ||
| Weight: Weight, | ||
| Balance: Balance, | ||
| Signer: blsInfo, | ||
| RemainingBalanceOwner: message.PChainOwner{ | ||
| Threshold: 1, | ||
| Addresses: addrs, | ||
| }, | ||
| } | ||
| bootstrapValidators = append(bootstrapValidators, bootstrapValidator) | ||
|
|
||
| convertSubnetParams := txs.ConvertSubnetToL1TxParams{ | ||
| // SubnetAuthKeys are the keys used to sign `ConvertSubnetToL1Tx` | ||
| SubnetAuthKeys: subnetAuthKeys, | ||
| // SubnetID is Subnet ID of the subnet to convert to an L1. | ||
| SubnetID: subnetID, | ||
| // ChainID is Blockchain ID of the L1 where the validator manager contract is deployed. | ||
| ChainID: chainID, | ||
| // Address is address of the validator manager contract. | ||
| Address: common.HexToAddress(validatorManagerAddr).Bytes(), | ||
| // Validators are the initial set of L1 validators after the conversion. | ||
| Validators: bootstrapValidators, | ||
| // Wallet is the wallet used to sign `ConvertSubnetToL1Tx` | ||
| Wallet: &wallet, // Use the wallet wrapper | ||
| } | ||
|
|
||
| tx, err := deployer.NewConvertSubnetToL1Tx(convertSubnetParams) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to create convert subnet tx: %w", err) | ||
| } | ||
|
|
||
| // Since it has the required signatures, we will now commit the transaction on chain | ||
| txID, err := deployer.Commit(*tx, wallet, true) | ||
| if err != nil { | ||
| return fmt.Errorf("failed to commit transaction: %w", err) | ||
| } | ||
| fmt.Printf("Convert subnet to L1 transaction submitted successfully! TX ID: %s\n", txID.String()) | ||
| return nil | ||
| } | ||
|
|
||
| func main() { | ||
| if err := ConvertL1(); err != nil { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nice |
||
| fmt.Println(err) | ||
| os.Exit(1) | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
| package txs | ||
|
|
||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why is this called baseTx.go? |
||
| import ( | ||
| "errors" | ||
|
|
||
| "github.com/ava-labs/avalanchego/ids" | ||
| "github.com/ava-labs/avalanchego/utils/set" | ||
| "github.com/ava-labs/avalanchego/vms/secp256k1fx" | ||
| "github.com/ava-labs/avalanchego/wallet/subnet/primary/common" | ||
|
|
||
| "github.com/ava-labs/avalanche-tooling-sdk-go/keychain" | ||
| "github.com/ava-labs/avalanche-tooling-sdk-go/network" | ||
| "github.com/ava-labs/avalanchego/wallet/subnet/primary" | ||
| ) | ||
|
|
||
| var ErrNoSubnetAuthKeysInWallet = errors.New("auth wallet does not contain auth keys") | ||
|
|
||
| type PublicDeployer struct { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets not have this object at all, make all function to receive the wallet There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. or just a pchain helper |
||
| kc *keychain.Keychain | ||
| network network.Network | ||
| wallet *primary.Wallet | ||
| } | ||
|
|
||
| func NewPublicDeployer(kc *keychain.Keychain, network network.Network) *PublicDeployer { | ||
| return &PublicDeployer{ | ||
| kc: kc, | ||
| network: network, | ||
| } | ||
| } | ||
|
|
||
| func (d *PublicDeployer) getMultisigTxOptions(subnetAuthKeys []ids.ShortID) []common.Option { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets move this function as a helper into wallet |
||
| options := []common.Option{} | ||
| walletAddrs := d.kc.Addresses().List() | ||
| changeAddr := walletAddrs[0] | ||
| // addrs to use for signing | ||
| customAddrsSet := set.Set[ids.ShortID]{} | ||
| customAddrsSet.Add(walletAddrs...) | ||
| customAddrsSet.Add(subnetAuthKeys...) | ||
| options = append(options, common.WithCustomAddresses(customAddrsSet)) | ||
| // set change to go to wallet addr (instead of any other subnet auth key) | ||
| changeOwner := &secp256k1fx.OutputOwners{ | ||
| Threshold: 1, | ||
| Addrs: []ids.ShortID{changeAddr}, | ||
| } | ||
| options = append(options, common.WithChangeOwner(changeOwner)) | ||
| return options | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| // Copyright (C) 2025, Ava Labs, Inc. All rights reserved. | ||
| // See the file LICENSE for licensing terms. | ||
| package txs | ||
|
|
||
| import ( | ||
| "context" | ||
| "fmt" | ||
|
|
||
| "github.com/ava-labs/avalanche-tooling-sdk-go/multisig" | ||
| "github.com/ava-labs/avalanche-tooling-sdk-go/wallet" | ||
|
|
||
| "github.com/ava-labs/avalanchego/ids" | ||
| "github.com/ava-labs/avalanchego/vms/platformvm/txs" | ||
| ) | ||
|
|
||
| // ConvertSubnetToL1TxParams contains all parameters needed to create a ConvertSubnetToL1Tx | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ok I agree with this structs to have more compat with TS, will also do in my PR |
||
| type ConvertSubnetToL1TxParams struct { | ||
| // SubnetAuthKeys are the keys used to sign `ConvertSubnetToL1Tx` | ||
| SubnetAuthKeys []ids.ShortID | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if you agree, we can incorporate the simplifications of strings instead of ids.ID, etc, so we are more |
||
| // SubnetID is Subnet ID of the subnet to convert to an L1. | ||
| SubnetID ids.ID | ||
| // ChainID is Blockchain ID of the L1 where the validator manager contract is deployed. | ||
| ChainID ids.ID | ||
| // Address is address of the validator manager contract. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. also this as string |
||
| Address []byte | ||
| // Validators are the initial set of L1 validators after the conversion. | ||
| Validators []*txs.ConvertSubnetToL1Validator | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this also will need replacement into a new struct that uses strings, etc |
||
| // Wallet is the wallet used to sign `ConvertSubnetToL1Tx` | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe wallet should like outside of this struct, and be a separate argument, as in my PR, so NewConvertSubnetToL1Tx will take the wallet/client, and the params. |
||
| Wallet *wallet.Wallet | ||
| } | ||
|
|
||
| func (d *PublicDeployer) NewConvertSubnetToL1Tx(params ConvertSubnetToL1TxParams) (*multisig.Multisig, error) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't believe we want to be this a member method of a PublicDeployer. lets make this function not |
||
| options := d.getMultisigTxOptions(params.SubnetAuthKeys) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets have this as a helper function of the wallet, but that does not modify the wallet (we already have that in our wallet but I believe it is best to not modify it) |
||
| unsignedTx, err := params.Wallet.P().Builder().NewConvertSubnetToL1Tx( | ||
| params.SubnetID, | ||
| params.ChainID, | ||
| params.Address, | ||
| params.Validators, | ||
| options..., | ||
| ) | ||
| if err != nil { | ||
| return nil, fmt.Errorf("error building tx: %w", err) | ||
| } | ||
| tx := txs.Tx{Unsigned: unsignedTx} | ||
| if err := params.Wallet.P().Signer().Sign(context.Background(), &tx); err != nil { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. lets avoid signing here, just return unsigned multisig |
||
| return nil, fmt.Errorf("error signing tx: %w", err) | ||
| } | ||
| return multisig.New(&tx), nil | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably the name should be NewSignerProofOfPossession.
Besides this, I don't think it belongs to blockchain/. It most probably belongs to utils/, or pchain/utils/