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

Jp/subsidized update profile #584

Draft
wants to merge 95 commits into
base: main
Choose a base branch
from
Draft

Conversation

poolcoke
Copy link
Contributor

@poolcoke poolcoke commented Apr 6, 2024

No description provided.

mattfoley8 and others added 30 commits April 7, 2023 13:32
* Allow ParamUpdater to update PoS GlobalParams.

* Add epoch number to get pos params call.

* Change core branch in test Dockerfile.

* Add test for updating global params.

* Fix bugs in testing updating global params.

* Checkout correct core branch.

* Change core branch.
* Add validator registration endpoints.

* Allow ExtraData to be logged.
* Add get validator by PublicKey route.

* Address PR review feedback.
* Rename VotingPublicKeySignature to VotingAuthorization.

* Checkout corresponding core branch.

* Change core branch in test CI.
* Refactor merging GlobalParamsEntry defaults.

* Change core branch in test dockerfile.
* [stable] Release 3.4.4

* add node version endpoint (#505)

Co-authored-by: Lazy Nina <>

* [stable] Release 3.4.5

* hotfix to exchange test

* Add captcha verification (#509)

* Updates to captcha verification

* Updates to backend

* Updates to captcha verify

* Update captcha verification

* Cleanup logs

* Add routes to store reward amount in global state, track usage via data dog

* Update verify captcha validation ordering, add back comp profile config bool

* Update badger sync settings to optimize memory usage during hypersync (#506)

* Update hypersync to use default badger settings, switch to performance settings once hypersync completes

* Update test dockerfile to accept core branch name as parameter

* Blank commit to trigger build

* ln/fix-transaction-info-mempool (#510)

Co-authored-by: Lazy Nina <>

* ln/no-comp-when-0-create-profile-fee (#511)

Co-authored-by: Lazy Nina <>

* Empty commit to trigger build (#515)

* Add extra data to basic transfer and diamond txn construction endpoints (#516)

* trigger build (#517)

Co-authored-by: Lazy Nina <>

* trigger build

* Add RWLock around AllCountryLevelSignUpBonuses (#518)

Co-authored-by: Lazy Nina <>

---------

Co-authored-by: Lazy Nina <>
Co-authored-by: superzordon <[email protected]>
Co-authored-by: Lazy Nina <>
Co-authored-by: Lazy Nina <>
…nstruction endpoint (#523)

Co-authored-by: Lazy Nina <>
* Add stake, unstake, and unlock stake txn construction endpoints

* Add stake, unstake, and unlock stake txn construction endpoints

---------

Co-authored-by: Lazy Nina <>
* Add spending limits backend support for stake, unstake, unlock stake

* Add txn construction and get endpoints for lockups

* Add additional sanity checks to lockup endpoint.

* Add txn construction and get endpoints for lockups

* Add additional sanity checks to lockup endpoint.

* Remove redundant profile entry response from LockedBalanceEntryResponse.

* Add proper timestamp to simulateSubmitTransaction.

* Apply suggestions from code review

---------

Co-authored-by: Lazy Nina <>
Co-authored-by: Jon Pollock <[email protected]>
Base automatically changed from jp/atomic_txns to feature/proof-of-stake April 9, 2024 18:46
lazynina and others added 22 commits April 9, 2024 14:48
* Simple CreateAtomicTxnsWrapper scaffolding.

* Update method on CreateAtomicTxnsWrapper.

* Use lib.GetAugmentedUniversalViewWithAdditionalTransactions for UpdateProfile.

* Use MaxTxnSizeBytesPoS for atomic transaction construction.

* Use updated lib.GetAugmentedUniversalViewWithAdditionalTransactions parameters.

* Gate preceding transactions middleware on POST requests.

* Use anonymous struct to simplify preceding transaction middleware.

* Update stateful transaction endpoints to use GetAugmentedUniversalViewWithAdditionalTransactions.

* Simple CreateAtomicTxnsWrapper scaffolding.

* Update method on CreateAtomicTxnsWrapper.

* Use MaxTxnSizeBytesPoS for atomic transaction construction.

* Fix MaxTxnSizeBytes bug

---------

Co-authored-by: diamondhands <[email protected]>
Co-authored-by: Lazy Nina <[email protected]>
// is equivalent to the sum of the fees paid and the sum of the basic transfers (outputs).
func ComputeNecessarySubsidyForTransaction(txn *lib.MsgDeSoTxn) uint64 {
necessarySubsidy := txn.TxnFeeNanos
for _, txOutput := range txn.TxOutputs {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We must delete this otherwise the wallet can be drained instantaneously.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure this would be accessible to the frontend. Isn't this a risk only if we accept transactions to subsidize from the frontend?

Currently transactions are constructed AND subsidized in the backend in the same endpoint, meaning ComputeNecessarySubsidyForTransaction should be safe. Happy to change it if we want though, could be bad to have this in general.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I make a request to subsidized-update-profile and add TransactionFees: [{ PublicKeyBase58Check: 'some-public-key', AmountNanos: 10*1e9}]. Now in line 875 of your transaction.go file, this gets added as an additional output in updateProfileTxn. updateProfileTxn is passed into ComputeNecessarySubsidyForTransaction and I've just taken 10 DESO from the subsidy wallet.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh yeah you're correct... didn't realize the transaction request data enabled the user to specify the transaction fees. Assumed we would compute that on the backend based on network conditions.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah they aren't network fees really - they allow app developers to easily request a amount of DESO when using their app to transact.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch!

Copy link
Member

@lazynina lazynina left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How can we prevent abuse of the subsidy endpoint? Nefarious actors could drain the wallet very quickly.

requestData.IsHidden,
additionalFees,
extraData,
requestData.MinFeeRateNanosPerKB,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should force this txn to be constructed with the global min fee rate, otherwise the subsidizing wallet could be drained very quickly.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

Comment on lines +76 to +214
fmt.Sprintf("SubmitAtomicTransaction: "+
"Problem deserializing atomic transaction from bytes: %v", err))
return
}
if atomicTxn.TxnMeta.GetTxnType() != lib.TxnTypeAtomicTxnsWrapper {
_AddBadRequestError(ww, fmt.Sprintf("SubmitAtomicTransaction: "+
"IncompleteAtomicTransaction must be an atomic transaction"))
return
}

// Create a map from the pre-signature inner transaction hash to DeSo signature.
innerTxnPreSignatureHashToSignature := make(map[lib.BlockHash]lib.DeSoSignature)
for ii, signedInnerTxnHex := range requestData.SignedInnerTransactionsHex {
// Decode the signed inner transaction.
signedTxnBytes, err := hex.DecodeString(signedInnerTxnHex)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf(
"SubmitAtomicTransaction: Problem decoding signed transaction hex: %v", err))
return
}

// Deserialize the signed transaction.
signedInnerTxn := &lib.MsgDeSoTxn{}
if err := signedInnerTxn.FromBytes(signedTxnBytes); err != nil {
_AddBadRequestError(ww, fmt.Sprintf(
"SubmitAtomicTransaction: Problem deserializing signed transaction %d from bytes: %v",
ii, err))
return
}

// Verify the signature is present.
if signedInnerTxn.Signature.Sign == nil {
_AddBadRequestError(ww, fmt.Sprintf(
"SubmitAtomicTransaction: Signed transaction %d hex missing signature", ii))
return
}

// Find the pre-signature DeSo transaction hash.
// NOTE: We do not use the lib.MsgDeSoTxn.Hash() function here as
// the transactions included in the atomic transaction do not yet
// have their signature fields set.
preSignatureInnerTxnBytes, err := signedInnerTxn.ToBytes(true)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf(
"SubmitAtomicTransaction: Problem serializing "+
"signed transaction %d without signature: %v", ii, err))
return
}
preSignatureInnerTxnHash := lib.Sha256DoubleHash(preSignatureInnerTxnBytes)
innerTxnPreSignatureHashToSignature[*preSignatureInnerTxnHash] = signedInnerTxn.Signature
}

// Based on the provided signatures, complete the atomic transaction.
for jj, innerTxn := range atomicTxn.TxnMeta.(*lib.AtomicTxnsWrapperMetadata).Txns {
// Skip signed inner transactions.
if innerTxn.Signature.Sign != nil {
continue
}

// Find the pre-signature DeSo transaction hash for this transaction.
preSignatureInnerTxnBytes, err := innerTxn.ToBytes(true)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf(
"SubmitAtomicTransaction: Problem serializing "+
"transaction %d of atomic transaction wrapper without signature: %v", jj, err))
return
}
preSignatureInnerTxnHash := lib.Sha256DoubleHash(preSignatureInnerTxnBytes)

// Check that we have the signature.
if _, exists := innerTxnPreSignatureHashToSignature[*preSignatureInnerTxnHash]; !exists {
_AddBadRequestError(ww, fmt.Sprintf(
"SubmitAtomicTransaction: Transaction %d in atomic transaction still missing signature", jj))
return
}

// Set the signature in the atomic transaction.
atomicTxn.TxnMeta.(*lib.AtomicTxnsWrapperMetadata).Txns[jj].Signature =
innerTxnPreSignatureHashToSignature[*preSignatureInnerTxnHash]
}

// Verify and broadcast the completed atomic transaction.
if err := fes.backendServer.VerifyAndBroadcastTransaction(atomicTxn); err != nil {
_AddBadRequestError(ww,
fmt.Sprintf("SubmitAtomicTransaction: Problem broadcasting transaction: %v", err))
return
}

res := &SubmitAtomicTransactionResponse{
Transaction: atomicTxn,
TxnHashHex: atomicTxn.Hash().String(),
TransactionIDBase58Check: lib.PkToString(atomicTxn.Hash()[:], fes.Params),
}

if err := json.NewEncoder(ww).Encode(res); err != nil {
_AddBadRequestError(ww, fmt.Sprintf(
"SubmitAtomicTransaction: Problem encoding response as JSON: %v", err))
return
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move this to a separate PR such that we can accept this change independently of the subsidization changes?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes! I'm going to leave this in until @diamondhands0 has a chance to read through everything.

Something we might consider is leaving this backend PR as a draft until we decide we need it. It shouldn't be too difficult to merge this PR in later if we wanted to. It's mostly just adding new endpoints and helper functions.

Comment on lines +817 to +830
// Grab subsidization keys.
subsidizationSeedBytes, err := bip39.NewSeedWithErrorChecking(fes.Config.TransactionSubsidizationSeed, "")
if err != nil {
_AddBadRequestError(ww,
fmt.Sprintf("SubsidizedUpdateProfile: Problem generating subsidization seed: %v", err))
return
}
subsidizationPublicKey, subsidizationPrivateKey, _, err :=
lib.ComputeKeysFromSeed(subsidizationSeedBytes, 0, fes.Params)
if err != nil {
_AddBadRequestError(ww,
fmt.Sprintf("SubsidizedUpdateProfile: Problem computing subsidization keys: %v", err))
return
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would move all the subsidization logic into its own function. This way we could enforce more strict checks around it and use it more broadly.

Copy link
Member

@diamondhands0 diamondhands0 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I reviewed this earlier and it's really great code, but I agree with @lazynina that we should keep it as a draft and merge it in later when we need it. At that time we can thoughtfully disable things like the additionalOutputs and guarantee that it's free of any issues.

I would add, though, that it's extremely valuable that you did this full end to end BE<>FE test @poolcoke because without it we would run the risk of having something critical missing in our implementation that would require another hard fork to fix.

// is equivalent to the sum of the fees paid and the sum of the basic transfers (outputs).
func ComputeNecessarySubsidyForTransaction(txn *lib.MsgDeSoTxn) uint64 {
necessarySubsidy := txn.TxnFeeNanos
for _, txOutput := range txn.TxOutputs {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great catch!

@poolcoke poolcoke marked this pull request as draft April 16, 2024 03:59
Base automatically changed from feature/proof-of-stake to main June 17, 2024 18:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants