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

BCI-2376 TXM as service. Post TX endpoint. #11256

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
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
16 changes: 16 additions & 0 deletions core/chains/evm/config/mocks/chain_scoped_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

37 changes: 29 additions & 8 deletions core/chains/evm/txmgr/evm_tx_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strings"
"time"

sq "github.com/Masterminds/squirrel"
Copy link
Contributor

Choose a reason for hiding this comment

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

Is this important to have? It doesn't look like we are using it for anything fancy, and it comes with some extra dependencies. Could we use regular SQL instead?

Copy link
Contributor

Choose a reason for hiding this comment

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

I can imagine some interesting uses though, and @krehermann might want to take a look

Copy link
Collaborator Author

@dhaidashenko dhaidashenko Nov 21, 2023

Choose a reason for hiding this comment

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

Yes, it's possible to implement a workaround, but it will be messier.
Let me know, if you'd like me to drop this dep

Copy link
Contributor

Choose a reason for hiding this comment

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

I didn't see all the logic at first, so I can see how it is helpful now, but for me it is still a little difficult to read and it seems inefficient too. Especially when reusing for the second query.

"github.com/ethereum/go-ethereum/common"
"github.com/google/uuid"
"github.com/jackc/pgconn"
Expand Down Expand Up @@ -55,7 +56,7 @@ type TxStoreWebApi interface {
FindTxByHash(hash common.Hash) (*Tx, error)
Transactions(offset, limit int) ([]Tx, int, error)
TxAttempts(offset, limit int) ([]TxAttempt, int, error)
TransactionsWithAttempts(offset, limit int) ([]Tx, int, error)
TransactionsWithAttempts(selector TransactionsWithAttemptsSelector) ([]Tx, int, error)
FindTxAttempt(hash common.Hash) (*TxAttempt, error)
FindTxWithAttempts(etxID int64) (etx Tx, err error)
}
Expand Down Expand Up @@ -440,18 +441,38 @@ func (o *evmTxStore) Transactions(offset, limit int) (txs []Tx, count int, err e
return
}

type TransactionsWithAttemptsSelector struct {
IdempotencyKey *string
Offset uint64
Limit uint64
}

// TransactionsWithAttempts returns all eth transactions with at least one attempt
// limited by passed parameters. Attempts are sorted by id.
func (o *evmTxStore) TransactionsWithAttempts(offset, limit int) (txs []Tx, count int, err error) {
sql := `SELECT count(*) FROM evm.txes WHERE id IN (SELECT DISTINCT eth_tx_id FROM evm.tx_attempts)`
if err = o.q.Get(&count, sql); err != nil {
return
func (o *evmTxStore) TransactionsWithAttempts(selector TransactionsWithAttemptsSelector) (txs []Tx, count int, err error) {
stmt := sq.Select("count(*)").From("evm.txes").
Where("id IN (SELECT DISTINCT eth_tx_id FROM evm.tx_attempts)").PlaceholderFormat(sq.Dollar)
if selector.IdempotencyKey != nil {
stmt = stmt.Where("idempotency_key = ?", *selector.IdempotencyKey)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why not just use string concatenation instead of squirrel?

}

sql = `SELECT * FROM evm.txes WHERE id IN (SELECT DISTINCT eth_tx_id FROM evm.tx_attempts) ORDER BY id desc LIMIT $1 OFFSET $2`
query, args, err := stmt.ToSql()
if err != nil {
return nil, 0, fmt.Errorf("failed to build count query: %w", err)
}

if err = o.q.Get(&count, query, args...); err != nil {
return nil, 0, fmt.Errorf("failed to exec count query: %w, sql: %s", err, query)
}

query, args, err = stmt.RemoveColumns().Columns("*").
OrderBy("id desc").Limit(selector.Limit).Offset(selector.Offset).ToSql()
Copy link
Contributor

Choose a reason for hiding this comment

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

@samsondav @reductionista WDYT about this github.com/Masterminds/squirrel library? It certainly helps in dynamic cases, but I'm worried about readability and efficiency.

Copy link
Collaborator

Choose a reason for hiding this comment

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

In my experience, SQL generators usually make things harder to read and understand than writing raw SQL. They sound nice in theory but don't work well in practice. Just my 2c

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmank88 @samsondav for this case, do you prefer strings builder/concatenation to handle the optional idempotency_key?

Copy link
Contributor

Choose a reason for hiding this comment

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

I think it would be worth writing out to compare side-by-side at least.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@jmank88 @samsondav

func (o *evmTxStore) TransactionsWithAttempts(selector TransactionsWithAttemptsSelector) (txs []Tx, count int, err error) {
	countQuery := "SELECT count(*) FROM evm.txes WHERE id IN (SELECT DISTINCT eth_tx_id FROM evm.tx_attempts)"
	selectQuery := "SELECT * FROM evm.txes WHERE id IN (SELECT DISTINCT eth_tx_id FROM evm.tx_attempts)"
	var args []interface{}
	if selector.IdempotencyKey != nil {
		args = append(args, *selector.IdempotencyKey)
		// NOTE: we are lucky to have only one optional filter and WHERE is the root statement, so we can use AND 
		selectQuery += " AND idempotency_key = ?"
		countQuery += " AND idempotency_key = ?"
	}

	countQuery = replacePositionalPlaceholders(countQuery)
	if err = o.q.Get(&count, countQuery, args...); err != nil {
		return nil, 0, fmt.Errorf("failed to exec count query: %w, sql: %s", err, countQuery)
	}

	args = append(args, selector.Limit, selector.Offset)
	selectQuery += " ORDER BY id desc LIMIT ? OFFSET ?"
	var dbTxs []DbEthTx
	selectQuery = replacePositionalPlaceholders(selectQuery)
	if err = o.q.Select(&dbTxs, selectQuery, args...); err != nil {
		return nil, 0, fmt.Errorf("failed to exec select query: %w, sql: %s", err, selectQuery)
	}
	txs = dbEthTxsToEvmEthTxs(dbTxs)
	err = o.preloadTxAttempts(txs)
	return
}

// Copy-paste from squirrel to replace ? with $1
func replacePositionalPlaceholders(sql string) string {
	buf := &bytes.Buffer{}
	i := 0
	for {
		p := strings.Index(sql, "?")
		if p == -1 {
			break
		}

		if len(sql[p:]) > 1 && sql[p:p+2] == "??" { // escape ?? => ?
			buf.WriteString(sql[:p])
			buf.WriteString("?")
			if len(sql[p:]) == 1 {
				break
			}
			sql = sql[p+2:]
		} else {
			i++
			buf.WriteString(sql[:p])
			fmt.Fprintf(buf, "$%d", i)
			sql = sql[p+1:]
		}
	}

	buf.WriteString(sql)
	return buf.String()
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Postgres uses numeric place holders for query arguments, so instead of ? we need to use $1, $2, etc.. So on the line
selectQuery += " ORDER BY id desc LIMIT ? OFFSET ?" we do not know which placeholders to use , as idempotency key is optional

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah OK. So then is this correct? Is there any way to simplify it more? Maybe a Sprintf would be easier to read?

func (o *evmTxStore) TransactionsWithAttempts(selector TransactionsWithAttemptsSelector) (txs []Tx, count int, err error) {
	query := " FROM evm.txes WHERE id IN (SELECT DISTINCT eth_tx_id FROM evm.tx_attempts)"
	var args []interface{}
	if selector.IdempotencyKey != nil {
		args = append(args, *selector.IdempotencyKey)
		// NOTE: we are lucky to have only one optional filter and WHERE is the root statement, so we can use AND 
		query += " AND idempotency_key = $1"
	}

	countQuery := "SELECT count(*)" + query
	if err = o.q.Get(&count, countQuery, args...); err != nil {
		return nil, 0, fmt.Errorf("failed to exec count query: %w, sql: %s", err, countQuery)
	}

	selectQuery := "SELECT * " + query + " ORDER BY id desc "
	if len(args) == 0 {
		selectQuery += "LIMIT $1 OFFSET $2"
	} else {
		selectQuery += "LIMIT $2 OFFSET $3"
	}
	args = append(args, selector.Limit, selector.Offset)
	var dbTxs []DbEthTx
	if err = o.q.Select(&dbTxs, selectQuery, args...); err != nil {
		return nil, 0, fmt.Errorf("failed to exec select query: %w, sql: %s", err, selectQuery)
	}
	txs = dbEthTxsToEvmEthTxs(dbTxs)
	err = o.preloadTxAttempts(txs)
	return
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Looks good, but I'm worried about this part

if len(args) == 0 {
		selectQuery += "LIMIT $1 OFFSET $2"
} else {
		selectQuery += "LIMIT $2 OFFSET $3"
}

What if we need to add another optional parameter? It does not seem to be easy to extend.
Why do you prefer this to the placeholder replacement?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure. @samsondav WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

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

sqlx comes with a helper to replace ? with $x. Does this solve your problem? https://github.com/jmoiron/sqlx/blob/master/bind.go#L60

if err != nil {
return nil, 0, fmt.Errorf("failed to build select query: %w", err)
}
var dbTxs []DbEthTx
if err = o.q.Select(&dbTxs, sql, limit, offset); err != nil {
return
if err = o.q.Select(&dbTxs, query, args...); err != nil {
return nil, 0, fmt.Errorf("failed to exec select query: %w, sql: %s", err, query)
}
txs = dbEthTxsToEvmEthTxs(dbTxs)
err = o.preloadTxAttempts(txs)
Expand Down
20 changes: 18 additions & 2 deletions core/chains/evm/txmgr/evm_tx_store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,10 @@ func TestORM_TransactionsWithAttempts(t *testing.T) {
require.NoError(t, err)
require.Equal(t, 3, count)

txs, count, err := txStore.TransactionsWithAttempts(0, 100) // should omit tx3
txs, count, err := txStore.TransactionsWithAttempts(txmgr.TransactionsWithAttemptsSelector{
Offset: 0,
Limit: 100,
}) // should omit tx3
require.NoError(t, err)
assert.Equal(t, 2, count, "only eth txs with attempts are counted")
assert.Len(t, txs, 2)
Expand All @@ -70,11 +73,24 @@ func TestORM_TransactionsWithAttempts(t *testing.T) {
assert.Equal(t, int64(3), *txs[0].TxAttempts[0].BroadcastBeforeBlockNum, "attempts should be sorted by created_at")
assert.Equal(t, int64(2), *txs[0].TxAttempts[1].BroadcastBeforeBlockNum, "attempts should be sorted by created_at")

txs, count, err = txStore.TransactionsWithAttempts(0, 1)
txs, count, err = txStore.TransactionsWithAttempts(txmgr.TransactionsWithAttemptsSelector{
Offset: 0,
Limit: 1,
})
require.NoError(t, err)
assert.Equal(t, 2, count, "only eth txs with attempts are counted")
assert.Len(t, txs, 1, "limit should apply to length of results")
assert.Equal(t, evmtypes.Nonce(1), *txs[0].Sequence, "transactions should be sorted by nonce")

txs, count, err = txStore.TransactionsWithAttempts(txmgr.TransactionsWithAttemptsSelector{
IdempotencyKey: tx2.IdempotencyKey,
Offset: 0,
Limit: 10,
})
require.NoError(t, err)
assert.Equal(t, 1, count, "only eth tx with specified idempotency key")
assert.Equal(t, tx2.ID, txs[0].ID)
assert.Equal(t, tx2.IdempotencyKey, txs[0].IdempotencyKey)
}

func TestORM_Transactions(t *testing.T) {
Expand Down
24 changes: 13 additions & 11 deletions core/chains/evm/txmgr/mocks/evm_tx_store.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions core/config/app_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type AppConfig interface {
Threshold() Threshold
WebServer() WebServer
Tracing() Tracing
TxmAsService() TxmAsService
}

type DatabaseBackupMode string
Expand Down
5 changes: 5 additions & 0 deletions core/config/docs/core.toml
Copy link
Contributor

@jmank88 jmank88 Nov 15, 2023

Choose a reason for hiding this comment

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

What is the rationale for feature flagging this service? If is meant to be temporary only, then we should use the existing [Feature]. If it is meant to be precautionary, and make sending txes opt-in only, then I think we have an inconsistency, because the existing endpoints to send tokens are not opt-in only (meaning we could just drop this and have it be enabled by default).

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes it's meant to be precautionary. Right now only planned user of this endpoint is Mercury Config Distribution project. Enabling by default this endpoint for all the nodes increases attack surface and we are skeptical that it'll pass security review.

Additionally it's planned to use this flag to only start services that are directly used by TXM to reduce resource redundant resources consumption and reduce RPC calls.

Copy link
Contributor

Choose a reason for hiding this comment

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

we are skeptical that it'll pass security review.

Even knowing that the ability to send tokens is already available? I am claiming that this is inconsistent.

Copy link
Contributor

Choose a reason for hiding this comment

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

Additionally it's planned to use this flag to only start services that are directly used by TXM to reduce resource redundant resources consumption and reduce RPC calls.

Can you elaborate on this? It sounds like an anti-pattern.

Copy link
Contributor

@jmank88 jmank88 Nov 15, 2023

Choose a reason for hiding this comment

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

Regardless, it sounds like [Feature] is more appropriate, and since this is user facing we might prefer TransactionService:

[Feature]
TransactionService = true

Copy link
Contributor

Choose a reason for hiding this comment

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

No point unnecessarily adding new endpoints publicly when not needed. (even though send tokens already exists)

There are tradeoffs, and these are not public.

Secondly, we wanted to use this feature also as a way of knowing when to only start TXM and dependencies, vs the regular Core Node dependencies. Are you saying that needs yet another config or feature?

This is misguided. A feature flag is scoped to a feature. This feature flag should not act as a global switch that runs the node in a different type of "mode". This is undesirable for the same reasons as a separate subcommand.

I'm not opposed to gating the new routes, but I'm still not clear on whether it would be temporary or not.

I am opposed to having this act like anything other than a simple gate for the new routes.

Copy link
Contributor

@prashantkumar1982 prashantkumar1982 Nov 20, 2023

Choose a reason for hiding this comment

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

For the new routes, what is the Foundation team's recommendation to use?

Treat this as permanent. We may spin off a new separate service in future, but for now, that is not even being planned.
So we need a way for Core Node to run in "TXM as a Service" mode, where:

  1. We only need TXM and its dependencies to be run. Other services are not needed, and preferably not started.
  2. New API capabilities (like Tx creation) to be only enabled in this mode, not in a regular Core Node mode.

There are tradeoffs, and these are not public

I'm talking from a Security point of view. Our code is open source. So anyone can see which endpoints are enabled by default. Thus we don't want to enable the newly created endpoints on all nodes run by all NOPs. In any case, we are fine to choose any option that Security team recommends here, as part of the Security Review.

Copy link
Contributor

Choose a reason for hiding this comment

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

For the new routes, what is the Foundation team's recommendation to use?

Regardless, it sounds like [Feature] is more appropriate, and since this is user facing we might prefer TransactionService:

[Feature]
TransactionService = true

Treat this as permanent. We may spin off a new separate service in future, but for now, that is not even being planned.

I mean, if this is feature flagged, is the feature flag switch permanent, or just temporary until the new routes are phased in and eventually enabled by default - after which we could remove the feature flag. I'm not sure it actually matters though, since there aren't any other config fields involved.

So we need a way for Core Node to run in "TXM as a Service" mode, where:

We only need TXM and its dependencies to be run. Other services are not needed, and preferably not started.
New API capabilities (like Tx creation) to be only enabled in this mode, not in a regular Core Node mode.

In what specific ways does a feature flag simply gating the routes fail to meet these goals? It should not be necessary to wire any other logic to this feature flag for unrelated services. If there are services running and using resources when not configured, we should fix those.

Copy link
Contributor

Choose a reason for hiding this comment

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

When "TransactionService = false", which will be the default, the service will operate as current Core Node.
When "TransactionService = true", which will deliberately be set like this by our internal EngOps, when they want to deploy the TransactionService, then it will run TXM as a Service, and enable new routes.

I would say, this setting will remain like this indefinitely. In future, when and if we take TXM as a Service into entirely a different service than Core Node, then we should be able to remove this feature flag from the Core Node code.

For other services, you are saying, they should be left untouched. If they cause issues, like resource consumption, then we ought to fix that?
Yes, we can start with that. Ultimately, when this scales to 100+ EVM chains supported, and 1 instance has to support multiple chains, we might have to revisit this. If un-needed services are causing issues, like resources, or unnecessary logging or anything else, we may want to explicitly turn them off.

Copy link
Contributor

Choose a reason for hiding this comment

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

Sure we can revisit this later. But my claim is that having "modes" at all is an anti-pattern and solves a problem which doesn't actually exist. We have the ability to configure many services independently, which enables complete flexibility for configuring any custom "mode" that is desired. There simply is no need to compromise this model.

Original file line number Diff line number Diff line change
Expand Up @@ -597,3 +597,8 @@ SamplingRatio = 1.0 # Example
[Tracing.Attributes]
# env is an example user specified key-value pair
env = "test" # Example

[TxmAsService]
# Enabled turns Transaction Manager as a service feature on or off. When enabled exposes endpoint to submit arbitrary EVM transaction
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: Also add that when enabled, regular Core Node services won't be run. Instead only TXM dependencies will be run.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Right now enabled does not affect which services are running. Should I introduce these changes as part of this PR?

# using one of the keys enabled for the specified chain.
Enabled = false # Default
12 changes: 12 additions & 0 deletions core/config/toml/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ type Core struct {
Sentry Sentry `toml:",omitempty"`
Insecure Insecure `toml:",omitempty"`
Tracing Tracing `toml:",omitempty"`
TxmAsService TxmAsService `toml:",omitempty"`
}

// SetFrom updates c with any non-nil values from f. (currently TOML field only!)
Expand Down Expand Up @@ -88,6 +89,7 @@ func (c *Core) SetFrom(f *Core) {
c.Sentry.setFrom(&f.Sentry)
c.Insecure.setFrom(&f.Insecure)
c.Tracing.setFrom(&f.Tracing)
c.TxmAsService.setFrom(&f.TxmAsService)
}

func (c *Core) ValidateConfig() (err error) {
Expand Down Expand Up @@ -1530,3 +1532,13 @@ func isValidURI(uri string) bool {
func isValidHostname(hostname string) bool {
return hostnameRegex.MatchString(hostname)
}

type TxmAsService struct {
Enabled *bool
}

func (t *TxmAsService) setFrom(f *TxmAsService) {
if v := f.Enabled; v != nil {
t.Enabled = f.Enabled
}
}
5 changes: 5 additions & 0 deletions core/config/txm_as_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package config

type TxmAsService interface {
Enabled() bool
}
2 changes: 2 additions & 0 deletions core/internal/cltest/factories.go
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,9 @@ func EmptyCLIContext() *cli.Context {
}

func NewEthTx(t *testing.T, fromAddress common.Address) txmgr.Tx {
idempotencyKey := uuid.New().String()
return txmgr.Tx{
IdempotencyKey: &idempotencyKey,
FromAddress: fromAddress,
ToAddress: testutils.NewAddress(),
EncodedPayload: []byte{1, 2, 3},
Expand Down
3 changes: 3 additions & 0 deletions core/scripts/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ require (
github.com/DataDog/zstd v1.5.2 // indirect
github.com/Depado/ginprom v1.7.11 // indirect
github.com/Masterminds/semver/v3 v3.2.1 // indirect
github.com/Masterminds/squirrel v1.5.4 // indirect
github.com/Microsoft/go-winio v0.6.1 // indirect
github.com/VictoriaMetrics/fastcache v1.10.0 // indirect
github.com/andres-erbsen/clock v0.0.0-20160526145045-9e14626cd129 // indirect
Expand Down Expand Up @@ -207,6 +208,8 @@ require (
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/lib/pq v1.10.9 // indirect
Expand Down
6 changes: 6 additions & 0 deletions core/scripts/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ github.com/Kubuxu/go-os-helper v0.0.1/go.mod h1:N8B+I7vPCT80IcP58r50u4+gEEcsZETF
github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM=
github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10=
github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
Expand Down Expand Up @@ -919,6 +921,10 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw=
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk=
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0/go.mod h1:vmVJ0l/dxyfGW6FmdpVm2joNMFikkuWg0EoCKLGUMNw=
github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a h1:dHCfT5W7gghzPtfsW488uPmEOm85wewI+ypUwibyTdU=
github.com/leanovate/gopter v0.2.10-0.20210127095200-9abe2343507a/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8=
github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
Expand Down
4 changes: 4 additions & 0 deletions core/services/chainlink/config_general.go
Original file line number Diff line number Diff line change
Expand Up @@ -518,4 +518,8 @@ func (g *generalConfig) Tracing() coreconfig.Tracing {
return &tracingConfig{s: g.c.Tracing}
}

func (g *generalConfig) TxmAsService() config.TxmAsService {
return &txmAsServiceConfig{c: g.c.TxmAsService}
}

var zeroSha256Hash = models.Sha256Hash{}
1 change: 1 addition & 0 deletions core/services/chainlink/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -666,6 +666,7 @@ func TestConfig_Marshal(t *testing.T) {
},
},
}
full.TxmAsService = toml.TxmAsService{Enabled: ptr(true)}

for _, tt := range []struct {
name string
Expand Down
16 changes: 16 additions & 0 deletions core/services/chainlink/config_txm_as_service.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package chainlink

import (
"github.com/smartcontractkit/chainlink/v2/core/config"
"github.com/smartcontractkit/chainlink/v2/core/config/toml"
)

var _ config.TxmAsService = (*txmAsServiceConfig)(nil)

type txmAsServiceConfig struct {
c toml.TxmAsService
}

func (t *txmAsServiceConfig) Enabled() bool {
return t.c.Enabled != nil && *t.c.Enabled
}
16 changes: 16 additions & 0 deletions core/services/chainlink/mocks/general_config.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,6 @@ Enabled = false
CollectorTarget = ''
NodeID = ''
SamplingRatio = 0.0

[TxmAsService]
Enabled = false
3 changes: 3 additions & 0 deletions core/services/chainlink/testdata/config-full.toml
Original file line number Diff line number Diff line change
Expand Up @@ -243,6 +243,9 @@ SamplingRatio = 1.0
env = 'dev'
test = 'load'

[TxmAsService]
Enabled = true

[[EVM]]
ChainID = '1'
Enabled = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,9 @@ CollectorTarget = ''
NodeID = ''
SamplingRatio = 0.0

[TxmAsService]
Enabled = false

[[EVM]]
ChainID = '1'
AutoCreateKey = true
Expand Down
12 changes: 12 additions & 0 deletions core/store/models/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -662,3 +662,15 @@ func (h ServiceHeader) Validate() (err error) {
}
return
}

// CreateEVMTransactionRequest represents a request to create request to submit evm transaction.
type CreateEVMTransactionRequest struct {
IdempotencyKey string `json:"idempotencyKey"`
ChainID *utils.Big `json:"chainID"`
ToAddress *common.Address `json:"toAddress"`
FromAddress common.Address `json:"fromAddress"` // optional, if not set we use one of accounts available for specified chain
EncodedPayload string `json:"encodedPayload"` // hex encoded payload
Value *utils.Big `json:"value"`
ForwarderAddress common.Address `json:"forwarderAddress"`
FeeLimit uint32 `json:"feeLimit"`
}
Loading
Loading