Skip to content

Commit

Permalink
Create implicit accout with funds from inx-faucet
Browse files Browse the repository at this point in the history
  • Loading branch information
jkrvivian committed Nov 1, 2023
1 parent aa11da1 commit db61bba
Show file tree
Hide file tree
Showing 7 changed files with 36 additions and 100 deletions.
4 changes: 2 additions & 2 deletions accountwallet/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func (a *AccountWallet) createAccountImplicitly(params *CreateAccountParams) (io
// An implicit account has an implicitly defined Block Issuer Key, corresponding to the address itself.
// Thus, implicit accounts can issue blocks by signing them with the private key corresponding to the public key
// from which the Implicit Account Creation Address was derived.
implicitAccountOutput, privateKey, err := a.getFunds(params.Amount, iotago.AddressImplicitAccountCreation)
implicitAccountOutput, privateKey, err := a.getFunds(iotago.AddressImplicitAccountCreation)
if err != nil {
return iotago.EmptyAccountID, ierrors.Wrap(err, "Failed to create account")
}
Expand Down Expand Up @@ -66,7 +66,7 @@ func (a *AccountWallet) transitionImplicitAccount(
AccountID(iotago.AccountIDFromOutputID(implicitAccountOutput.OutputID)).
BlockIssuer(blockIssuerKeys, iotago.MaxSlotIndex).MustBuild()

log.Infof("Created account %s with %d tokens\n", accountOutput.AccountID.ToHex(), params.Amount)
log.Infof("Created account %s with %d tokens\n", accountOutput.AccountID.ToHex())
txBuilder := a.createTransactionBuilder(implicitAccountOutput, implicitAccAddr, accountOutput)

// TODO get congestionResponse from API
Expand Down
1 change: 0 additions & 1 deletion accountwallet/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ type AccountSubcommands interface {

type CreateAccountParams struct {
Alias string
Amount uint64
NoBIF bool
Implicit bool
Transition bool
Expand Down
117 changes: 28 additions & 89 deletions accountwallet/faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"github.com/mr-tron/base58"

"github.com/iotaledger/evil-tools/models"
"github.com/iotaledger/hive.go/core/safemath"
"github.com/iotaledger/hive.go/ierrors"
"github.com/iotaledger/hive.go/lo"
"github.com/iotaledger/iota-core/pkg/blockhandler"
Expand Down Expand Up @@ -39,32 +38,47 @@ func (a *AccountWallet) RequestBlockBuiltData(clt *nodeclient.Client, issuerID i
return congestionResp, issuerResp, version, nil
}

func (a *AccountWallet) RequestFaucetFunds(clt models.Client, receiveAddr iotago.Address, amount iotago.BaseToken) (*models.Output, error) {
congestionResp, issuerResp, version, err := a.RequestBlockBuiltData(clt.Client(), a.faucet.account.ID())
func (a *AccountWallet) RequestFaucetFunds(clt models.Client, receiveAddr iotago.Address) (*models.Output, error) {
//nolint:all,forcetypassert
err := clt.RequestFaucetFunds(receiveAddr)
if err != nil {
return nil, ierrors.Wrapf(err, "failed to get block built data for issuer %s", a.faucet.account.ID().ToHex())
return nil, ierrors.Wrap(err, "failed to request funds from faucet")
}

signedTx, err := a.faucet.prepareFaucetRequest(receiveAddr, amount, congestionResp.ReferenceManaCost)
indexer, err := clt.Indexer()
if err != nil {
log.Errorf("failed to prepare faucet request: %s", err)

return nil, err
return nil, ierrors.Wrap(err, "failed to get indexer client")
}

_, err = a.PostWithBlock(clt, signedTx, a.faucet.account, congestionResp, issuerResp, version)
addrBech := receiveAddr.Bech32(clt.CommittedAPI().ProtocolParameters().Bech32HRP())

time.Sleep(10 * time.Second)

res, err := indexer.Outputs(context.Background(), &apimodels.BasicOutputsQuery{
AddressBech32: addrBech,
})
if err != nil {
log.Errorf("failed to create block: %s", err)
return nil, ierrors.Wrap(err, "indexer request failed in request faucet funds")
}

var outputStruct iotago.Output
var outputID iotago.OutputID
for res.Next() {
unspents, err := res.Outputs(context.TODO())
if err != nil {
return nil, ierrors.Wrap(err, "failed to get faucet unspent outputs")
}

return nil, err
outputStruct = unspents[0]
outputID = lo.Return1(res.Response.Items.OutputIDs())[0]
}

return &models.Output{
OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), 0),
OutputID: outputID,
Address: receiveAddr,
AddressIndex: 0,
Balance: signedTx.Transaction.Outputs[0].BaseTokenAmount(),
OutputStruct: signedTx.Transaction.Outputs[0],
Balance: outputStruct.BaseTokenAmount(),
OutputStruct: outputStruct,
}, nil
}

Expand Down Expand Up @@ -204,78 +218,3 @@ func (f *faucet) getGenesisOutputFromIndexer(clt models.Client, faucetAddr iotag

return faucetUnspentOutput, faucetUnspentOutputID, faucetAmount, nil
}

func (f *faucet) prepareFaucetRequest(receiveAddr iotago.Address, amount iotago.BaseToken, rmc iotago.Mana) (*iotago.SignedTransaction, error) {
txBuilder, remainderIndex, err := f.createFaucetTransactionNoManaHandling(receiveAddr, amount)
if err != nil {
return nil, err
}

rmcAllottedTxBuilder := txBuilder.Clone()
// faucet will allot exact mana to be burnt, rest of the mana is alloted to faucet output remainder
rmcAllottedTxBuilder.AllotRequiredManaAndStoreRemainingManaInOutput(txBuilder.CreationSlot(), rmc, f.account.ID(), remainderIndex)

var signedTx *iotago.SignedTransaction
signedTx, err = rmcAllottedTxBuilder.Build(f.genesisHdWallet.AddressSigner())
if err != nil {
log.Infof("WARN: failed to build tx with min required mana allotted, genesis potential mana was not enough, fallback to faucet account")
txBuilder.AllotAllMana(txBuilder.CreationSlot(), f.account.ID())
if signedTx, err = txBuilder.Build(f.genesisHdWallet.AddressSigner()); err != nil {
return nil, ierrors.Wrapf(err, "failed to build transaction with all mana allotted, after not having enough mana required based on RMC")
}
}

// set remainder output to be reused by the Faucet wallet
//nolint:all,forcetypassert
f.unspentOutput = &models.Output{
OutputID: iotago.OutputIDFromTransactionIDAndIndex(lo.PanicOnErr(signedTx.Transaction.ID()), uint16(remainderIndex)),
Address: f.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address),
AddressIndex: 0,
Balance: signedTx.Transaction.Outputs[1].BaseTokenAmount(),
OutputStruct: signedTx.Transaction.Outputs[1],
}

return signedTx, nil
}

func (f *faucet) createFaucetTransactionNoManaHandling(receiveAddr iotago.Address, amount iotago.BaseToken) (*builder.TransactionBuilder, int, error) {
currentTime := time.Now()
currentSlot := f.clt.LatestAPI().TimeProvider().SlotFromTime(currentTime)

apiForSlot := f.clt.APIForSlot(currentSlot)
txBuilder := builder.NewTransactionBuilder(apiForSlot)

//nolint:all,forcetypassert
txBuilder.AddInput(&builder.TxInput{
UnlockTarget: f.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address),
InputID: f.unspentOutput.OutputID,
Input: f.unspentOutput.OutputStruct,
})

remainderAmount, err := safemath.SafeSub(f.unspentOutput.Balance, amount)
if err != nil {
return nil, 0, ierrors.Errorf("safeSub failed %d - %d: %s", f.unspentOutput.Balance, amount, err)
}

txBuilder.AddOutput(&iotago.BasicOutput{
Amount: amount,
Conditions: iotago.BasicOutputUnlockConditions{
&iotago.AddressUnlockCondition{Address: receiveAddr},
},
Features: iotago.BasicOutputFeatures{},
})

// remainder output
remainderIndex := 1
//nolint:all,forcetypassert
txBuilder.AddOutput(&iotago.BasicOutput{
Amount: remainderAmount,
Conditions: iotago.BasicOutputUnlockConditions{
&iotago.AddressUnlockCondition{Address: f.genesisHdWallet.Address(iotago.AddressEd25519).(*iotago.Ed25519Address)},
},
})
txBuilder.AddTaggedDataPayload(&iotago.TaggedData{Tag: []byte("Faucet funds"), Data: []byte("to addr" + receiveAddr.String())})
txBuilder.SetCreationSlot(currentSlot)

return txBuilder, remainderIndex, nil
}
4 changes: 2 additions & 2 deletions accountwallet/wallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,10 +280,10 @@ func (a *AccountWallet) isAccountReady(accData *models.AccountData) bool {
return true
}

func (a *AccountWallet) getFunds(amount uint64, addressType iotago.AddressType) (*models.Output, ed25519.PrivateKey, error) {
func (a *AccountWallet) getFunds(addressType iotago.AddressType) (*models.Output, ed25519.PrivateKey, error) {
receiverAddr, privKey, usedIndex := a.getAddress(addressType)

createdOutput, err := a.RequestFaucetFunds(a.client, receiverAddr, iotago.BaseToken(amount))
createdOutput, err := a.RequestFaucetFunds(a.client, receiverAddr)
if err != nil {
return nil, nil, ierrors.Wrap(err, "failed to request funds from Faucet")
}
Expand Down
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ func accountsSubcommand(wallet *accountwallet.AccountWallet, sub accountwallet.A
return
}

log.Infof("Created account %s with %d tokens", accountID, params.Amount)
log.Infof("Created account %s", accountID)

case accountwallet.OperationDestroyAccount:
log.Infof("Run subcommand: %s, with parametetr set: %v", accountwallet.OperationDestroyAccount, sub)
Expand Down
4 changes: 2 additions & 2 deletions models/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -219,7 +219,7 @@ type Client interface {
// GetCongestion returns congestion data such as rmc or issuing readiness.
GetCongestion(id iotago.AccountID) (resp *apimodels.CongestionResponse, err error)
// RequestFaucetFunds
RequestFaucetFunds(address *iotago.Ed25519Address) (err error)
RequestFaucetFunds(address iotago.Address) (err error)

iotago.APIProvider
}
Expand Down Expand Up @@ -279,7 +279,7 @@ func NewWebClient(url, faucetURL string, opts ...options.Option[WebClient]) (*We
}), initErr
}

func (c *WebClient) RequestFaucetFunds(address *iotago.Ed25519Address) (err error) {
func (c *WebClient) RequestFaucetFunds(address iotago.Address) (err error) {
req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, c.faucetURL+"/api/enqueue", func() io.Reader {
jsonData, _ := json.Marshal(&faucet.EnqueueRequest{
Address: address.Bech32(c.client.CommittedAPI().ProtocolParameters().Bech32HRP()),
Expand Down
4 changes: 1 addition & 3 deletions parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,6 @@ func accountUsage() {
func parseCreateAccountFlags(subcommands []string) (*accountwallet.CreateAccountParams, error) {
flagSet := flag.NewFlagSet("create", flag.ExitOnError)
alias := flagSet.String("alias", "", "The alias name of new created account")
amount := flagSet.Int64("amount", 1000, "The amount to be transferred to the new account")
noBif := flagSet.Bool("noBIF", false, "Create account without Block Issuer Feature, can only be set false no if implicit is false, as each account created implicitly needs to have BIF.")
implicit := flagSet.Bool("implicit", false, "Create an implicit account")
transition := flagSet.Bool("transition", true, "Indicates if account should be transitioned to full account if created with implicit address.")
Expand All @@ -253,7 +252,7 @@ func parseCreateAccountFlags(subcommands []string) (*accountwallet.CreateAccount
*noBif = false
}

log.Infof("Parsed flags: alias: %s, amount: %d, BIF: %t, implicit: %t, transition: %t", *alias, *amount, *noBif, *implicit, *transition)
log.Infof("Parsed flags: alias: %s, BIF: %t, implicit: %t, transition: %t", *alias, *noBif, *implicit, *transition)

if !*implicit == *transition {
log.Info("WARN: Implicit flag set to false, account will be created non-implicitly by Faucet, no need for transition, flag will be ignored")
Expand All @@ -262,7 +261,6 @@ func parseCreateAccountFlags(subcommands []string) (*accountwallet.CreateAccount

return &accountwallet.CreateAccountParams{
Alias: *alias,
Amount: uint64(*amount),
NoBIF: *noBif,
Implicit: *implicit,
Transition: *transition,
Expand Down

0 comments on commit db61bba

Please sign in to comment.