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

Mana initializer plugin and faucet web API endpoint #2296

Merged
merged 7 commits into from
Jun 21, 2022
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
10 changes: 5 additions & 5 deletions client/evilwallet/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,8 @@ type Client interface {
GetOutput(outputID utxo.OutputID) devnetvm.Output
// GetOutputGoF gets the first unspent outputs of a given address.
GetOutputGoF(outputID utxo.OutputID) gof.GradeOfFinality
// SendFaucetRequest requests funds from the faucet and returns the faucet request message ID.
SendFaucetRequest(address string) error
// BroadcastFaucetRequest requests funds from the faucet and returns the faucet request message ID.
BroadcastFaucetRequest(address string) error
// GetTransactionOutputs returns the outputs the transaction created.
GetTransactionOutputs(txID string) (outputs devnetvm.Outputs, err error)
// GetTransaction gets the transaction.
Expand Down Expand Up @@ -232,9 +232,9 @@ func (c *WebClient) SleepRateSetterEstimate() (err error) {
return nil
}

// SendFaucetRequest requests funds from the faucet and returns the faucet request message ID.
func (c *WebClient) SendFaucetRequest(address string) (err error) {
_, err = c.api.SendFaucetRequest(address, -1)
// BroadcastFaucetRequest requests funds from the faucet and returns the faucet request message ID.
func (c *WebClient) BroadcastFaucetRequest(address string) (err error) {
_, err = c.api.BroadcastFaucetRequest(address, -1)
return
}

Expand Down
2 changes: 1 addition & 1 deletion client/evilwallet/evilwallet.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ func (e *EvilWallet) requestFaucetFunds(wallet *Wallet) (outputID utxo.OutputID,
if err = RateSetterSleep(clt, true); err != nil {
return
}
err = clt.SendFaucetRequest(addr.Base58())
err = clt.BroadcastFaucetRequest(addr.Base58())
if err != nil {
return
}
Expand Down
56 changes: 48 additions & 8 deletions client/faucet.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (

"github.com/cockroachdb/errors"
"github.com/iotaledger/hive.go/identity"
"github.com/mr-tron/base58"

"github.com/iotaledger/goshimmer/packages/faucet"
"github.com/iotaledger/goshimmer/packages/jsonmodels"
Expand All @@ -16,16 +15,17 @@ import (
)

const (
routeFaucet = "faucet"
routeFaucetRequestBroadcast = "faucetrequest"
routeFaucetRequestAPI = "faucet"
)

var (
defaultPOWTarget = 25
powWorker = pow.New(1)
)

// SendFaucetRequest requests funds from faucet nodes by sending a faucet request payload message.
func (api *GoShimmerAPI) SendFaucetRequest(base58EncodedAddr string, powTarget int, pledgeIDs ...string) (*jsonmodels.FaucetResponse, error) {
// BroadcastFaucetRequest requests funds from faucet nodes by sending a faucet request payload message.
func (api *GoShimmerAPI) BroadcastFaucetRequest(base58EncodedAddr string, powTarget int, pledgeIDs ...string) (*jsonmodels.FaucetRequestResponse, error) {
var aManaPledgeID identity.ID
var cManaPledgeID identity.ID
if len(pledgeIDs) > 1 {
Expand All @@ -49,12 +49,52 @@ func (api *GoShimmerAPI) SendFaucetRequest(base58EncodedAddr string, powTarget i
return nil, errors.Errorf("could not compute faucet PoW: %w", err)
}

res := &jsonmodels.FaucetResponse{}
if err := api.do(http.MethodPost, routeFaucet,
res := &jsonmodels.FaucetRequestResponse{}
if err := api.do(http.MethodPost, routeFaucetRequestBroadcast,
&jsonmodels.FaucetRequest{
Address: base58EncodedAddr,
AccessManaPledgeID: base58.Encode(aManaPledgeID.Bytes()),
ConsensusManaPledgeID: base58.Encode(cManaPledgeID.Bytes()),
AccessManaPledgeID: aManaPledgeID.EncodeBase58(),
ConsensusManaPledgeID: cManaPledgeID.EncodeBase58(),
Nonce: nonce,
}, res); err != nil {
return nil, err
}

return res, nil
}

// SendFaucetRequestAPI requests funds from faucet nodes by sending a faucet request directly to the faucet node.
func (api *GoShimmerAPI) SendFaucetRequestAPI(base58EncodedAddr string, powTarget int, accessPledgeID, consensusPledgeID string) (*jsonmodels.FaucetAPIResponse, error) {
var aManaPledgeID identity.ID
var cManaPledgeID identity.ID
if accessPledgeID == "" && consensusPledgeID == "" {
return nil, errors.Errorf("accessPledgeID and consensusPledgeID must not be empty")
}
aManaPledgeIDFromString, err := mana.IDFromStr(accessPledgeID)
if err == nil {
aManaPledgeID = aManaPledgeIDFromString
}
cManaPledgeIDFromString, err := mana.IDFromStr(consensusPledgeID)
if err == nil {
cManaPledgeID = cManaPledgeIDFromString
}

address, err := devnetvm.AddressFromBase58EncodedString(base58EncodedAddr)
if err != nil {
return nil, errors.Errorf("could not decode address from string: %w", err)
}

nonce, err := computeFaucetPoW(address, aManaPledgeID, cManaPledgeID, powTarget)
if err != nil {
return nil, errors.Errorf("could not compute faucet PoW: %w", err)
}

res := &jsonmodels.FaucetAPIResponse{}
if err := api.do(http.MethodPost, routeFaucetRequestAPI,
&jsonmodels.FaucetRequest{
Address: base58EncodedAddr,
AccessManaPledgeID: aManaPledgeID.EncodeBase58(),
ConsensusManaPledgeID: cManaPledgeID.EncodeBase58(),
Nonce: nonce,
}, res); err != nil {
return nil, err
Expand Down
2 changes: 1 addition & 1 deletion client/wallet/webconnector.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (webConnector *WebConnector) RequestFaucetFunds(addr address.Address, powTa
return err
}

_, err = webConnector.client.SendFaucetRequest(addr.Address().Base58(), powTarget)
_, err = webConnector.client.BroadcastFaucetRequest(addr.Address().Base58(), powTarget)

return
}
Expand Down
3 changes: 3 additions & 0 deletions config.default.json
Original file line number Diff line number Diff line change
Expand Up @@ -93,5 +93,8 @@
},
"networkdelay": {
"originPublicKey": "9DB3j9cWYSuEEtkvanrzqkzCQMdH1FGv3TawJdVbDxkd"
},
"manaInitializer": {
"FaucetAPI": "faucet-01.devnet.shimmer.iota.cafe:8080"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ services:
--metrics.local=false
--metrics.global=true
--node.enablePlugins=analysisServer,analysisDashboard,prometheus
--node.disablePlugins=activity,analysisClient,chat,consensus,dashboard,faucet,fpc,gossip,firewall,issuer,mana,manualpeering,messageLayer,metrics,networkdelay,portcheck,pow,syncBeaconFollower,webAPIBroadcastDataEndpoint,WebAPIDataEndpoint,WebAPIHealthzEndpoint,WebAPIFaucetEndpoint,webAPIFindTransactionHashesEndpoint,webAPIGetNeighborsEndpoint,webAPIGetTransactionObjectsByHashEndpoint,webAPIGetTransactionTrytesByHashEndpoint,WebAPIInfoEndpoint,WebAPILedgerstateEndpoint,WebAPIMessageEndpoint,WebAPIToolsMessageEndpoint,WebAPIWeightProviderEndpoint,remotelog,remotelogmetrics,DAGsVisualizer,WebAPIRateSetterEndpoint,WebAPISchedulerEndpoint
--node.disablePlugins=activity,analysisClient,chat,consensus,dashboard,faucet,fpc,gossip,firewall,issuer,mana,manualpeering,messageLayer,metrics,networkdelay,portcheck,pow,syncBeaconFollower,webAPIBroadcastDataEndpoint,WebAPIDataEndpoint,WebAPIHealthzEndpoint,WebAPIFaucetRequestEndpoint,webAPIFindTransactionHashesEndpoint,webAPIGetNeighborsEndpoint,webAPIGetTransactionObjectsByHashEndpoint,webAPIGetTransactionTrytesByHashEndpoint,WebAPIInfoEndpoint,WebAPILedgerstateEndpoint,WebAPIMessageEndpoint,WebAPIToolsMessageEndpoint,WebAPIWeightProviderEndpoint,remotelog,remotelogmetrics,DAGsVisualizer,WebAPIRateSetterEndpoint,WebAPISchedulerEndpoint,ManaInitializer
--logger.level={{ logLevel }}
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,5 @@ services:
{% endif %}
--autoPeering.entryNodes=
--analysis.client.serverAddress=
--node.disablePlugins=activity,analysisClient,chat,consensus,dashboard,faucet,fpc,gossip,firewall,issuer,mana,manualpeering,messageLayer,metrics,networkdelay,portcheck,pow,syncBeaconFollower,webAPIBroadcastDataEndpoint,WebAPIDataEndpoint,WebAPIHealthzEndpoint,WebAPIFaucetEndpoint,webAPIFindTransactionHashesEndpoint,webAPIGetNeighborsEndpoint,webAPIGetTransactionObjectsByHashEndpoint,webAPIGetTransactionTrytesByHashEndpoint,WebAPIInfoEndpoint,WebAPILedgerstateEndpoint,WebAPIMessageEndpoint,WebAPIToolsMessageEndpoint,WebAPIWeightProviderEndpoint,remotelog,remotelogmetrics,DAGsVisualizer,WebAPIRateSetterEndpoint,WebAPISchedulerEndpoint
--node.disablePlugins=activity,analysisClient,chat,consensus,dashboard,faucet,fpc,gossip,firewall,issuer,mana,manualpeering,messageLayer,metrics,networkdelay,portcheck,pow,syncBeaconFollower,webAPIBroadcastDataEndpoint,WebAPIDataEndpoint,WebAPIHealthzEndpoint,WebAPIFaucetRequestEndpoint,webAPIFindTransactionHashesEndpoint,webAPIGetNeighborsEndpoint,webAPIGetTransactionObjectsByHashEndpoint,webAPIGetTransactionTrytesByHashEndpoint,WebAPIInfoEndpoint,WebAPILedgerstateEndpoint,WebAPIMessageEndpoint,WebAPIToolsMessageEndpoint,WebAPIWeightProviderEndpoint,remotelog,remotelogmetrics,DAGsVisualizer,WebAPIRateSetterEndpoint,WebAPISchedulerEndpoint,ManaInitializer
--logger.level={{ logLevel }}
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ services:
{% if networkVersion|default(None) %}
--autoPeering.networkVersion={{ networkVersion }}
{% endif %}
--node.disablePlugins=portcheck
--node.enablePlugins=dashboard,remotelog,networkdelay,prometheus{% if faucet|default(false) %},faucet{% endif %},activity,snapshot,WebAPIToolsMessageEndpoint,"WebAPI tools Endpoint"{% if spammer|default(false) %},spammer{% endif %}
--node.disablePlugins=portcheck,ManaInitializer
--node.enablePlugins=dashboard,remotelog,networkdelay,prometheus{% if faucet|default(false) %},faucet,WebAPIFaucetEndpoint{% endif %},activity,snapshot,WebAPIToolsMessageEndpoint,"WebAPI tools Endpoint"{% if spammer|default(false) %},spammer{% endif %}
--prometheus.bindAddress=0.0.0.0:9311
--activity.broadcastInterval=1s
{% if faucet|default(false) %}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ services:
{% if networkVersion|default(None) %}
--autoPeering.networkVersion={{ networkVersion }}
{% endif %}
--node.disablePlugins=portcheck
--node.disablePlugins=portcheck,ManaInitializer
--node.enablePlugins=dashboard,remotelog,networkdelay,prometheus{% if faucet|default(false) %},faucet{% endif %}
--prometheus.bindAddress=0.0.0.0:9311
{% if faucet|default(false) %}
Expand Down
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ services:
--node.enablePlugins=remotelog,networkdelay,spammer,prometheus
--database.directory=/app/mainnetdb
--node.peerDBDirectory=/app/peerdb
--manaInitializer.FaucetAPI=faucet-01.devnet.shimmer.iota.cafe:8080
--logger.level=info
--logger.disableEvents=false
--logger.remotelog.serverAddress=metrics-01.devnet.shimmer.iota.cafe:5213
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -473,8 +473,6 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/iotaledger/hive.go v0.0.0-20220609121824-f3f1126b2ee5 h1:sLn77ugsOD0uBqO+J9dPZn/s4vhEStQ1z/IdnRwcpl0=
github.com/iotaledger/hive.go v0.0.0-20220609121824-f3f1126b2ee5/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI=
github.com/iotaledger/hive.go v0.0.0-20220617100616-12bd6f35b4a1 h1:7eOS9j+1K2M66OLgL/7tYafQIP9d0MMmyn0e72AmRUA=
github.com/iotaledger/hive.go v0.0.0-20220617100616-12bd6f35b4a1/go.mod h1:8f9U7qHFby0W3cxv/nKnz9LHn9BbwWU0tMsWDnfqzRI=
github.com/ipfs/go-cid v0.0.1/go.mod h1:GHWU/WuQdMPmIosc4Yn1bcCT7dSeX4lBafM7iqUPQvM=
Expand Down
10 changes: 10 additions & 0 deletions packages/faucet/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,16 @@ func (p *Payload) ConsensusManaPledgeID() identity.ID {
return p.M.ConsensusManaPledgeID
}

// SetAccessManaPledgeID sets the access mana pledge ID of the faucet request.
func (p *Payload) SetAccessManaPledgeID(id identity.ID) {
p.M.AccessManaPledgeID = id
}

// SetConsensusManaPledgeID sets the consensus mana pledge ID of the faucet request.
func (p *Payload) SetConsensusManaPledgeID(id identity.ID) {
p.M.ConsensusManaPledgeID = id
}

// IsFaucetReq checks if the message is faucet payload.
func IsFaucetReq(msg *tangle.Message) bool {
return msg.Payload().Type() == RequestType
Expand Down
10 changes: 8 additions & 2 deletions packages/jsonmodels/faucet.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
package jsonmodels

// FaucetResponse contains the ID of the message sent.
type FaucetResponse struct {
// FaucetRequestResponse contains the ID of the message sent.
type FaucetRequestResponse struct {
ID string `json:"id,omitempty"`
Error string `json:"error,omitempty"`
}

// FaucetAPIResponse contains the status of facet request through web API.
type FaucetAPIResponse struct {
Success bool `json:"success"`
Error string `json:"error,omitempty"`
}

// FaucetRequest contains the address to request funds from faucet.
type FaucetRequest struct {
Address string `json:"address"`
Expand Down
2 changes: 2 additions & 0 deletions plugins/core.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
"github.com/iotaledger/goshimmer/plugins/gracefulshutdown"
"github.com/iotaledger/goshimmer/plugins/logger"
"github.com/iotaledger/goshimmer/plugins/manaeventlogger"
"github.com/iotaledger/goshimmer/plugins/manainitializer"
"github.com/iotaledger/goshimmer/plugins/manualpeering"
"github.com/iotaledger/goshimmer/plugins/messagelayer"
"github.com/iotaledger/goshimmer/plugins/metrics"
Expand Down Expand Up @@ -48,4 +49,5 @@ var Core = node.Plugins(
metrics.Plugin,
spammer.Plugin,
manaeventlogger.Plugin,
manainitializer.Plugin,
)
104 changes: 72 additions & 32 deletions plugins/faucet/plugin.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/iotaledger/hive.go/daemon"
"github.com/iotaledger/hive.go/generics/event"
"github.com/iotaledger/hive.go/generics/orderedmap"
"github.com/iotaledger/hive.go/identity"
"github.com/iotaledger/hive.go/node"
"github.com/iotaledger/hive.go/workerpool"
"github.com/mr-tron/base58"
Expand Down Expand Up @@ -110,9 +111,10 @@ func configure(plugin *node.Plugin) {
_faucet = newFaucet()

fundingWorkerPool = workerpool.NewNonBlockingQueuedWorkerPool(func(task workerpool.Task) {
msg := task.Param(0).(*tangle.Message)
addr := msg.Payload().(*faucet.Payload).Address()
msg, txID, err := _faucet.FulFillFundingRequest(msg)
faucetRequest := task.Param(0).(*faucet.Payload)
addr := faucetRequest.Address()

msg, txID, err := _faucet.FulFillFundingRequest(faucetRequest)
if err != nil {
plugin.LogWarnf("couldn't fulfill funding request to %s: %s", addr.Base58(), err)
return
Expand Down Expand Up @@ -208,19 +210,34 @@ func configureEvents() {
deps.Tangle.ApprovalWeightManager.Events.MessageProcessed.Attach(event.NewClosure(func(event *tangle.MessageProcessedEvent) {
onMessageProcessed(event.MessageID)
}))

deps.Tangle.TimeManager.Events.SyncChanged.Attach(event.NewClosure(func(event *tangle.SyncChangedEvent) {
if event.Synced {
synced <- true
}
}))
}

func onMessageProcessed(messageID tangle.MessageID) {
func OnWebAPIRequest(fundingRequest *faucet.Payload) error {
// Do not start picking up request while waiting for initialization.
// If faucet nodes crashes and you restart with a clean db, all previous faucet req msgs will be enqueued
// and addresses will be funded again. Therefore, do not process any faucet request messages until we are in
// sync and initialized.
if !initDone.Load() {
return errors.New("faucet plugin is not done initializing")
}

if err := handleFaucetRequest(fundingRequest); err != nil {
return err
}

return nil
}

func onMessageProcessed(messageID tangle.MessageID) {
// Do not start picking up request while waiting for initialization.
// If faucet nodes crashes, and you restart with a clean db, all previous faucet req msgs will be enqueued
// and addresses will be funded again. Therefore, do not process any faucet request messages until we are in
// sync and initialized.
if !initDone.Load() {
return
}
Expand All @@ -229,39 +246,62 @@ func onMessageProcessed(messageID tangle.MessageID) {
return
}
fundingRequest := message.Payload().(*faucet.Payload)
addr := fundingRequest.Address()

requestBytes, err := fundingRequest.Bytes()
if err != nil {
Plugin.LogInfof("couldn't serialize faucet request: %w", err)
return
// pledge mana to requester if not specified in the request
emptyID := identity.ID{}
if fundingRequest.AccessManaPledgeID() == emptyID {
fundingRequest.SetAccessManaPledgeID(identity.NewID(message.IssuerPublicKey()))
}
// verify PoW
leadingZeroes, err := powVerifier.LeadingZeros(requestBytes)
if err != nil {
Plugin.LogInfof("couldn't verify PoW of funding request for address %s: %w", addr.Base58(), err)
return
if fundingRequest.ConsensusManaPledgeID() == emptyID {
fundingRequest.SetConsensusManaPledgeID(identity.NewID(message.IssuerPublicKey()))
}

if leadingZeroes < targetPoWDifficulty {
Plugin.LogInfof("funding request for address %s doesn't fulfill PoW requirement %d vs. %d", addr.Base58(), targetPoWDifficulty, leadingZeroes)
return
}
_ = handleFaucetRequest(fundingRequest)
})
}

if IsAddressBlackListed(addr) {
Plugin.LogInfof("can't fund address %s since it is blacklisted", addr.Base58())
return
}
func handleFaucetRequest(fundingRequest *faucet.Payload) error {
addr := fundingRequest.Address()

// finally add it to the faucet to be processed
_, added := fundingWorkerPool.TrySubmit(message)
if !added {
RemoveAddressFromBlacklist(addr)
Plugin.LogInfof("dropped funding request for address %s as queue is full", addr.Base58())
return
}
Plugin.LogInfof("enqueued funding request for address %s", addr.Base58())
})
if !isFaucetRequestPoWValid(fundingRequest, addr) {
return errors.New("PoW requirement is not satisfied")
}

if IsAddressBlackListed(addr) {
Plugin.LogInfof("can't fund address %s since it is blacklisted", addr.Base58())
return errors.Newf("can't fund address %s since it is blacklisted %s", addr.Base58())
}

// finally add it to the faucet to be processed
_, added := fundingWorkerPool.TrySubmit(fundingRequest)
if !added {
RemoveAddressFromBlacklist(addr)
Plugin.LogInfof("dropped funding request for address %s as queue is full", addr.Base58())
return errors.Newf("dropped funding request for address %s as queue is full", addr.Base58())
}

Plugin.LogInfof("enqueued funding request for address %s", addr.Base58())
return nil
}

func isFaucetRequestPoWValid(fundingRequest *faucet.Payload, addr devnetvm.Address) bool {
requestBytes, err := fundingRequest.Bytes()
if err != nil {
Plugin.LogInfof("couldn't serialize faucet request: %w", err)
return false
}
// verify PoW
leadingZeroes, err := powVerifier.LeadingZeros(requestBytes)
if err != nil {
Plugin.LogInfof("couldn't verify PoW of funding request for address %s: %w", addr.Base58(), err)
return false
}
if leadingZeroes < targetPoWDifficulty {
Plugin.LogInfof("funding request for address %s doesn't fulfill PoW requirement %d vs. %d", addr.Base58(), targetPoWDifficulty, leadingZeroes)
return false
}

return true
}

// IsAddressBlackListed returns if an address is blacklisted.
Expand Down
Loading