diff --git a/Makefile b/Makefile index 66ad9ec2..d3f4e557 100644 --- a/Makefile +++ b/Makefile @@ -9,7 +9,8 @@ TINYGO_TARGETS := \ wasmx-fsm:28.finite_state_machine.wasm \ wasmx-raft-lib:2a.raft_library.wasm \ wasmx-raftp2p-lib:36.raftp2p_library.wasm \ - wasmx-ondemand-single-lib:65.wasmx_ondemand_single_library.wasm + wasmx-ondemand-single-lib:65.wasmx_ondemand_single_library.wasm \ + emailchain:63.wasmx_email_0.0.1.wasm # wasmx-gov:35.gov_0.0.1.wasm \ # wasmx-gov-continuous:37.gov_cont_0.0.1.wasm \ diff --git a/mythos-wasmedge/cmd/mythosd/main.go b/mythos-wasmedge/cmd/mythosd/main.go index 0b8deda5..6815c45a 100644 --- a/mythos-wasmedge/cmd/mythosd/main.go +++ b/mythos-wasmedge/cmd/mythosd/main.go @@ -10,8 +10,32 @@ import ( "github.com/loredanacirstea/wasmx/app" cmd "github.com/loredanacirstea/wasmx/cmdutils" + + vmpostgresql "github.com/loredanacirstea/wasmx-vmpostgresql" + "github.com/loredanacirstea/wasmx/x/network/vmcrosschain" + "github.com/loredanacirstea/wasmx/x/network/vmmc" + "github.com/loredanacirstea/wasmx/x/network/vmp2p" + "github.com/loredanacirstea/wasmx/x/vmkv" + "github.com/loredanacirstea/wasmx/x/vmsql" ) +func init() { + // enabled VM extensions for contracts + vmp2p.Setup() + vmmc.Setup() + + // experimental WIP, do not enable in production: + vmcrosschain.Setup() + vmsql.Setup() + vmpostgresql.Setup() + vmkv.Setup() + // vmimap.Setup() + // vmsmtp.Setup() + // vmhttpclient.Setup() + // vmhttpserver.Setup() + // vmoauth2client.Setup() +} + func main() { rootCmd, _ := cmd.NewRootCmd(&runtime.WasmEdgeVmMeta{}, app.DefaultNodeHome, nil) if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil { diff --git a/mythos-wazero/cmd/mythosd/main.go b/mythos-wazero/cmd/mythosd/main.go index 17cddde6..43f619fd 100644 --- a/mythos-wazero/cmd/mythosd/main.go +++ b/mythos-wazero/cmd/mythosd/main.go @@ -6,12 +6,41 @@ import ( svrcmd "github.com/cosmos/cosmos-sdk/server/cmd" + vmimap "github.com/loredanacirstea/wasmx-vmimap" + vmsmtp "github.com/loredanacirstea/wasmx-vmsmtp" runtime "github.com/loredanacirstea/wasmx-wazero" "github.com/loredanacirstea/wasmx/app" cmd "github.com/loredanacirstea/wasmx/cmdutils" + + vmpostgresql "github.com/loredanacirstea/wasmx-vmpostgresql" + "github.com/loredanacirstea/wasmx/x/network/vmcrosschain" + "github.com/loredanacirstea/wasmx/x/network/vmmc" + "github.com/loredanacirstea/wasmx/x/network/vmp2p" + "github.com/loredanacirstea/wasmx/x/vmhttpclient" + "github.com/loredanacirstea/wasmx/x/vmhttpserver" + "github.com/loredanacirstea/wasmx/x/vmkv" + "github.com/loredanacirstea/wasmx/x/vmoauth2client" + "github.com/loredanacirstea/wasmx/x/vmsql" ) +func init() { + // enabled VM extensions for contracts + vmp2p.Setup() + vmmc.Setup() + + // experimental WIP, do not enable in production: + vmcrosschain.Setup() + vmsql.Setup() + vmpostgresql.Setup() + vmkv.Setup() + vmimap.Setup() + vmsmtp.Setup() + vmhttpclient.Setup() + vmhttpserver.Setup() + vmoauth2client.Setup() +} + func main() { rootCmd, _ := cmd.NewRootCmd(runtime.NewWazeroVmMeta(), app.DefaultNodeHome, nil) if err := svrcmd.Execute(rootCmd, "", app.DefaultNodeHome); err != nil { diff --git a/tests/testdata/tinygo/README.md b/tests/testdata/tinygo/README.md index 5f168fcb..95ea0d6c 100644 --- a/tests/testdata/tinygo/README.md +++ b/tests/testdata/tinygo/README.md @@ -30,7 +30,7 @@ cd imaptest && GOWORK=off tinygo build -o ../imaptest.wasm -no-debug -scheduler= cd smtptest && GOWORK=off tinygo build -o ../smtptest.wasm -no-debug -scheduler=none -gc=leaking -target=wasi . && cd .. -cd emailchain && GOWORK=off tinygo build -o ../emailchain.wasm -no-debug -scheduler=none -gc=leaking -target=wasi . && cd .. +cd emailchain && GOWORK=off tinygo build -o ../emailchain.wasm -no-debug -scheduler=none -gc=leaking -target=wasi ./cmd && cd .. cd mailsrv && GOWORK=off tinygo build -o ../mailsrv.wasm -no-debug -scheduler=none -gc=leaking -target=wasi . && cd .. diff --git a/tests/testdata/tinygo/contracts.go b/tests/testdata/tinygo/contracts.go index 0e7f39d5..79d28073 100644 --- a/tests/testdata/tinygo/contracts.go +++ b/tests/testdata/tinygo/contracts.go @@ -22,7 +22,4 @@ var ( //go:embed smtptest.wasm SmtpTestWrapSdk []byte - - //go:embed emailchain.wasm - EmailChain []byte ) diff --git a/tests/testdata/tinygo/emailchain.wasm b/tests/testdata/tinygo/emailchain.wasm deleted file mode 100644 index edd81352..00000000 Binary files a/tests/testdata/tinygo/emailchain.wasm and /dev/null differ diff --git a/tests/testdata/tinygo/emailchain/actions_smtp_server.go b/tests/testdata/tinygo/emailchain/actions_smtp_server.go deleted file mode 100644 index fbf0cd80..00000000 --- a/tests/testdata/tinygo/emailchain/actions_smtp_server.go +++ /dev/null @@ -1,17 +0,0 @@ -package main - -import ( - wasmx "github.com/loredanacirstea/wasmx-env/lib" -) - -// TODO implement me -func HandleSmtpLogin(req *LoginRequest) ([]byte, error) { - if req.Password != "123456" { - wasmx.Revert([]byte(`unauthorized`)) - } - return nil, nil -} - -func HandleSmtpLogout(req *LogoutRequest) ([]byte, error) { - return nil, nil -} diff --git a/tests/testdata/tinygo/emailchain/main.go b/tests/testdata/tinygo/emailchain/cmd/main.go similarity index 63% rename from tests/testdata/tinygo/emailchain/main.go rename to tests/testdata/tinygo/emailchain/cmd/main.go index e8a2452b..73cbdd3b 100644 --- a/tests/testdata/tinygo/emailchain/main.go +++ b/tests/testdata/tinygo/emailchain/cmd/main.go @@ -2,11 +2,12 @@ package main import ( "encoding/json" - "fmt" + "os" - _ "github.com/loredanacirstea/wasmx-env-httpclient" - vmimap "github.com/loredanacirstea/wasmx-env-imap" - vmsmtp "github.com/loredanacirstea/wasmx-env-smtp" + lib "github.com/loredanacirstea/emailchain/lib" + _ "github.com/loredanacirstea/wasmx-env-httpclient/lib" + vmimap "github.com/loredanacirstea/wasmx-env-imap/lib" + vmsmtp "github.com/loredanacirstea/wasmx-env-smtp/lib" wasmx "github.com/loredanacirstea/wasmx-env/lib" ) @@ -16,22 +17,42 @@ func Wasmx_nondeterministic_1() {} //go:wasm-module wasmx //export memory_ptrlen_i64_1 -func Wemory_ptrlen_i64_1() {} +func Memory_ptrlen_i64_1() {} //go:wasm-module wasmx //export wasmx_env_i64_2 func Wasmx_env_i64_2() {} -//go:wasm-module emailchain -//export instantiate -func Instantiate() { - InitializeTables(ConnectionId) -} +//go:wasm-module smtp +//export wasmx_smtp_i64_1 +func Wasmx_smtp_i64_1() {} + +//go:wasm-module imap +//export wasmx_imap_i64_1 +func Wasmx_imap_i64_1() {} + +//go:wasm-module httpclient +//export wasmx_httpclient_i64_1 +func Wasmx_httpclient_i64_1() {} + +//go:wasm-module sql +//export wasmx_sql_i64_1 +func Wasmx_sql_i64_1() {} func main() { + entrypoint := os.Getenv("ENTRY_POINT") + // these entrypoints are internal by default + switch entrypoint { + case "smtp_update": + SmtpUpdate() + return + case "instantiate": + lib.InitializeTables(lib.ConnectionId) + return + } + databz := wasmx.GetCallData() - // fmt.Println("---databz----!!!!!!", string(databz)) - calld := &Calldata{} + calld := &lib.Calldata{} err := json.Unmarshal(databz, calld) if err != nil { wasmx.Revert([]byte(err.Error())) @@ -59,37 +80,35 @@ func main() { } response, _ = json.Marshal(&resp) } else if calld.SignDKIM != nil { - resp := SignDKIM(calld.SignDKIM) + resp := lib.SignDKIM(calld.SignDKIM) response, _ = json.Marshal(&resp) } else if calld.VerifyDKIM != nil { - resp := VerifyDKIM(calld.VerifyDKIM) + resp := lib.VerifyDKIM(calld.VerifyDKIM) response, _ = json.Marshal(&resp) } else if calld.VerifyARC != nil { - resp := VerifyARC(calld.VerifyARC) + resp := lib.VerifyARC(calld.VerifyARC) response, _ = json.Marshal(&resp) } else if calld.SignARC != nil { - resp := SignARC(calld.SignARC) + resp := lib.SignARC(calld.SignARC) response, _ = json.Marshal(&resp) } else if calld.ForwardEmail != nil { - resp := ForwardEmail(calld.ForwardEmail) + resp := lib.ForwardEmail(calld.ForwardEmail) response, _ = json.Marshal(&resp) } else if calld.CreateAccount != nil { - CreateAccount(calld.CreateAccount) + lib.CreateAccount(calld.CreateAccount) } else if calld.SendEmail != nil { - resp := SendEmail(calld.SendEmail) + resp := lib.SendEmail(calld.SendEmail) response, _ = json.Marshal(&resp) } else if calld.BuildAndSend != nil { - resp := BuildAndSend(calld.BuildAndSend) + resp := lib.BuildAndSend(calld.BuildAndSend) response, _ = json.Marshal(&resp) } else if calld.StartServer != nil { - StartServer(calld.StartServer) + lib.StartServer(calld.StartServer) } else if calld.IncomingEmail != nil { - IncomingEmail(calld.IncomingEmail) + lib.IncomingEmail(calld.IncomingEmail) } else if calld.RoleChanged != nil { - // TODO - // utils.OnlyRole(MODULE_NAME, roles.ROLE_ROLES, "RoleChanged") - // roleChanged(calld.RoleChanged); - InitializeTables(ConnectionId) + wasmx.OnlyRole(lib.MODULE_NAME, wasmx.ROLE_ROLES, "RoleChanged") + lib.InitializeTables(lib.ConnectionId) } else { handled := ImapServerRequest(calld) if handled { @@ -104,18 +123,15 @@ func main() { wasmx.SetFinishData(response) } -//go:wasm-module emailchain -//export smtp_update func SmtpUpdate() { - fmt.Println("---SmtpUpdate----!!!!!!") databz := wasmx.GetCallData() - calld := &ReentryCalldata{} + calld := &lib.ReentryCalldata{} err := json.Unmarshal(databz, calld) if err != nil { wasmx.Revert([]byte(err.Error())) } if calld.IncomingEmail != nil { - IncomingEmail(calld.IncomingEmail) + lib.IncomingEmail(calld.IncomingEmail) } } @@ -134,52 +150,52 @@ func prepareResponse(data []byte, ierr *vmimap.Error, err error) []byte { return bz } -func ImapServerRequest(calld *Calldata) bool { +func ImapServerRequest(calld *lib.Calldata) bool { // var res interface{} var res []byte switch { case calld.Login != nil: - data, err := HandleLogin(calld.Login) + data, err := lib.HandleLogin(calld.Login) res = prepareResponse(data, nil, err) case calld.Logout != nil: - data, err := HandleLogout(calld.Logout) + data, err := lib.HandleLogout(calld.Logout) res = prepareResponse(data, nil, err) case calld.Create != nil: - data, ierr, err := HandleCreate(calld.Create) + data, ierr, err := lib.HandleCreate(calld.Create) res = prepareResponse(data, ierr, err) case calld.Delete != nil: - data, err := HandleDelete(calld.Delete) + data, err := lib.HandleDelete(calld.Delete) res = prepareResponse(data, nil, err) case calld.Rename != nil: - data, err := HandleRename(calld.Rename) + data, err := lib.HandleRename(calld.Rename) res = prepareResponse(data, nil, err) case calld.Select != nil: - data, err := HandleSelect(calld.Select) + data, err := lib.HandleSelect(calld.Select) res = prepareResponse(data, nil, err) case calld.List != nil: - data, err := HandleList(calld.List) + data, err := lib.HandleList(calld.List) res = prepareResponse(data, nil, err) case calld.Status != nil: - data, err := HandleStatus(calld.Status) + data, err := lib.HandleStatus(calld.Status) res = prepareResponse(data, nil, err) case calld.Append != nil: - data, err := HandleAppend(calld.Append) + data, err := lib.HandleAppend(calld.Append) res = prepareResponse(data, nil, err) case calld.Expunge != nil: - data, err := HandleExpunge(calld.Expunge) + data, err := lib.HandleExpunge(calld.Expunge) res = prepareResponse(data, nil, err) case calld.Search != nil: - data, err := HandleSearch(calld.Search) + data, err := lib.HandleSearch(calld.Search) res = prepareResponse(data, nil, err) case calld.Fetch != nil: - data, err := HandleFetch(calld.Fetch) + data, err := lib.HandleFetch(calld.Fetch) res = prepareResponse(data, nil, err) case calld.Store != nil: - data, err := HandleStore(calld.Store) + data, err := lib.HandleStore(calld.Store) res = prepareResponse(data, nil, err) case calld.Copy != nil: - data, err := HandleCopy(calld.Copy) + data, err := lib.HandleCopy(calld.Copy) res = prepareResponse(data, nil, err) default: return false @@ -188,14 +204,14 @@ func ImapServerRequest(calld *Calldata) bool { return true } -func SmtpServerRequest(calld *Calldata) bool { +func SmtpServerRequest(calld *lib.Calldata) bool { var res []byte switch { case calld.Login != nil: - data, err := HandleSmtpLogin(calld.Login) + data, err := lib.HandleSmtpLogin(calld.Login) res = prepareResponse(data, nil, err) case calld.Logout != nil: - data, err := HandleSmtpLogout(calld.Logout) + data, err := lib.HandleSmtpLogout(calld.Logout) res = prepareResponse(data, nil, err) default: return false diff --git a/tests/testdata/tinygo/emailchain/go.mod b/tests/testdata/tinygo/emailchain/go.mod index 2dd459e4..286b16fc 100644 --- a/tests/testdata/tinygo/emailchain/go.mod +++ b/tests/testdata/tinygo/emailchain/go.mod @@ -16,10 +16,9 @@ require ( require ( cosmossdk.io/math v1.5.3 // indirect - github.com/loredanacirstea/wasmx-utils v0.0.0 // indirect + github.com/loredanacirstea/wasmx-env-utils v0.0.0 // indirect golang.org/x/net v0.42.0 // indirect golang.org/x/text v0.27.0 // indirect - github.com/loredanacirstea/wasmx-env-utils v0.0.0 // indirect ) replace github.com/loredanacirstea/wasmx-env-sql => ../wasmx-env-sql diff --git a/tests/testdata/tinygo/emailchain/actions.go b/tests/testdata/tinygo/emailchain/lib/actions.go similarity index 89% rename from tests/testdata/tinygo/emailchain/actions.go rename to tests/testdata/tinygo/emailchain/lib/actions.go index 39cb25f3..c0133a83 100644 --- a/tests/testdata/tinygo/emailchain/actions.go +++ b/tests/testdata/tinygo/emailchain/lib/actions.go @@ -1,4 +1,4 @@ -package main +package lib import ( "bufio" @@ -17,12 +17,12 @@ import ( "github.com/loredanacirstea/mailverif/dns" "github.com/loredanacirstea/mailverif/forward" "github.com/loredanacirstea/mailverif/utils" - sql "github.com/loredanacirstea/wasmx-env-sql" + sql "github.com/loredanacirstea/wasmx-env-sql/lib" - "github.com/loredanacirstea/emailchain/imap" + "github.com/loredanacirstea/emailchain/lib/imap" - vmimap "github.com/loredanacirstea/wasmx-env-imap" - vmsmtp "github.com/loredanacirstea/wasmx-env-smtp" + vmimap "github.com/loredanacirstea/wasmx-env-imap/lib" + vmsmtp "github.com/loredanacirstea/wasmx-env-smtp/lib" wasmx "github.com/loredanacirstea/wasmx-env/lib" ) @@ -47,12 +47,23 @@ func StartServer(req *StartServerRequest) { if err != nil { wasmx.Revert([]byte(err.Error())) } + + // Store the server password + if req.Password == "" { + wasmx.Revert([]byte("server password cannot be empty")) + } + StoreServerPassword(req.Password) + + // Store the Kayros bot no-store setting + StoreKayrosBotNoStore(req.KayrosBotNoStore) + smtpCfg := requiredSmtpDefaults(req.Smtp) // SMTP 25 // smtpCfg.Addr = fmt.Sprintf("%s:%s", smtpCfg.Domain, PortSmtpDirectAddr) smtpCfg.Addr = fmt.Sprintf("%s:%s", "", PortSmtpDirectAddr) smtpCfg.StartTLS = true + // prevents your server from being an "open relay" (can't forward spam) smtpCfg.EnableAuth = false resp := vmsmtp.ServerStart(&vmsmtp.ServerStartRequest{ ConnectionId: PortSmtpDirectAddr, @@ -99,7 +110,6 @@ func StartServer(req *StartServerRequest) { ServerConfig: req.Imap, }) if resp2.Error != "" { - fmt.Println("---IMAP--", resp2.Error) vmsmtp.ServerClose(&vmsmtp.ServerCloseRequest{ConnectionId: PortSmtpDirectAddr}) vmsmtp.ServerClose(&vmsmtp.ServerCloseRequest{ConnectionId: PortSmtpClientTls}) vmsmtp.ServerClose(&vmsmtp.ServerCloseRequest{ConnectionId: PortSmtpClientStartTls}) @@ -113,7 +123,6 @@ func StartServer(req *StartServerRequest) { ServerConfig: req.Imap, }) if resp2.Error != "" { - fmt.Println("---IMAP--", resp2.Error) vmsmtp.ServerClose(&vmsmtp.ServerCloseRequest{ConnectionId: PortSmtpDirectAddr}) vmsmtp.ServerClose(&vmsmtp.ServerCloseRequest{ConnectionId: PortSmtpClientTls}) vmsmtp.ServerClose(&vmsmtp.ServerCloseRequest{ConnectionId: PortSmtpClientStartTls}) @@ -123,8 +132,6 @@ func StartServer(req *StartServerRequest) { } func CreateAccount(req *CreateAccountRequest) { - fmt.Println("--CreateAccount--", req.Username) - err := ConnectSql(ConnectionId) if err != nil { wasmx.Revert([]byte("CreateAccount: DB connection failed: " + err.Error())) @@ -174,6 +181,7 @@ func CreateAccount(req *CreateAccountRequest) { } func IncomingEmail(req *IncomingEmailRequest) { + wasmx.LoggerInfo(MODULE_NAME, "incoming email", []string{}) if len(req.From) == 0 { wasmx.Revert([]byte("incoming email: empty from")) } @@ -192,7 +200,7 @@ func IncomingEmail(req *IncomingEmailRequest) { return } if len(owners) == 0 { - wasmx.Revert([]byte("invalid account")) + wasmx.Revert([]byte("invalid account: " + to)) return } } @@ -213,21 +221,58 @@ func IncomingEmail(req *IncomingEmailRequest) { // if this a forwarded email, we do a check and add a header with the result timestamp := time.Unix(req.Timestamp, 0).UTC() emailraw := ApplyForwardCheck(req.EmailRaw, timestamp) + + // Check if Kayros bot no-store is enabled + kayrosBotNoStore := LoadKayrosBotNoStore() + isBotEmail := IsBotEmail(req.To) + + // Store received emails (unless it's for Kayros bot and no-store is enabled) // TODO get ipfrom from Received headers added by MTAs (Gmail, etc.) - for _, to := range req.To { - err = StoreEmail(to, req.From, emailraw, req.IpFrom, ConnectionId, FolderInbox) + if !isBotEmail || !kayrosBotNoStore { + for _, to := range req.To { + err = StoreEmail(to, req.From, emailraw, req.IpFrom, ConnectionId, FolderInbox) + if err != nil { + fmt.Println(err) + } + } + } + + // Check if email is for Kayros bot and handle it + if isBotEmail { + err := HandleKayrosBot(req) if err != nil { - fmt.Println(err) + fmt.Printf("Kayros bot error: %v\n", err) } } } else { + // This is an email sent by an authenticated email client to our smtp server + // Validate authenticated user matches the sender + if req.AuthenticatedUser == "" { + wasmx.Revert([]byte("unauthorized: authentication required for sending emails")) + return + } + if req.AuthenticatedUser != req.From[0] { + wasmx.Revert([]byte("unauthorized: authenticated user (" + req.AuthenticatedUser + ") does not match sender (" + req.From[0] + ")")) + return + } + + // Validate that the sender has an account on this server + owners, err := GetAccount(req.From[0]) + if err != nil { + wasmx.Revert([]byte("HandleEmail: failed to check sender account: " + err.Error())) + return + } + if len(owners) == 0 { + wasmx.Revert([]byte("unauthorized: sender account not found: " + req.From[0])) + return + } + folder := FolderSent opts := LoadDkimKey() if opts == nil { fmt.Println("no dkim keys") folder = FolderDraft } else { - // this is an email sent by an email client to our smtp server errs := SendRawEmail(req.From[0], req.To, req.EmailRaw, *opts) if err != nil { fmt.Println(errs) diff --git a/tests/testdata/tinygo/emailchain/actions_imap_server.go b/tests/testdata/tinygo/emailchain/lib/actions_imap_server.go similarity index 94% rename from tests/testdata/tinygo/emailchain/actions_imap_server.go rename to tests/testdata/tinygo/emailchain/lib/actions_imap_server.go index d7b0a896..0ff0a577 100644 --- a/tests/testdata/tinygo/emailchain/actions_imap_server.go +++ b/tests/testdata/tinygo/emailchain/lib/actions_imap_server.go @@ -1,4 +1,4 @@ -package main +package lib import ( "encoding/json" @@ -6,31 +6,37 @@ import ( "strings" "time" - imap "github.com/loredanacirstea/emailchain/imap" - vmimap "github.com/loredanacirstea/wasmx-env-imap" - sql "github.com/loredanacirstea/wasmx-env-sql" + imap "github.com/loredanacirstea/emailchain/lib/imap" + vmimap "github.com/loredanacirstea/wasmx-env-imap/lib" + sql "github.com/loredanacirstea/wasmx-env-sql/lib" wasmx "github.com/loredanacirstea/wasmx-env/lib" ) func HandleLogin(req *LoginRequest) ([]byte, error) { - if req.Password != "123456" { - wasmx.Revert([]byte(`unauthorized`)) - } - // err := ConnectSql(ConnectionId) - // if err != nil { - // return nil, err - // } - // params, err := paramsMarshal([]sql.SqlQueryParam{ - // {Type: "text", Value: req.Username}, - // }) - // resp := sql.Execute(&sql.SqlExecuteRequest{ - // Id: ConnectionId, - // Query: `INSERT OR IGNORE INTO owners (address) VALUES (?);`, - // Params: params, - // }) - // if resp.Error != "" { - // return nil, fmt.Errorf(resp.Error) - // } + // Connect to database to check if username exists + err := ConnectSql(ConnectionId) + if err != nil { + wasmx.Revert([]byte("HandleLogin: DB connection failed: " + err.Error())) + } + + // Validate username exists in database + owners, err := GetAccount(req.Username) + if err != nil { + wasmx.Revert([]byte("HandleLogin: failed to check account: " + err.Error())) + } + if len(owners) == 0 { + wasmx.Revert([]byte("unauthorized: invalid username")) + } + + // Validate password against stored server password + storedPassword := LoadServerPassword() + if storedPassword == "" { + wasmx.Revert([]byte("unauthorized: server password not configured")) + } + if req.Password != storedPassword { + wasmx.Revert([]byte("unauthorized: invalid password")) + } + return nil, nil } diff --git a/tests/testdata/tinygo/emailchain/lib/actions_smtp_server.go b/tests/testdata/tinygo/emailchain/lib/actions_smtp_server.go new file mode 100644 index 00000000..f1c2788a --- /dev/null +++ b/tests/testdata/tinygo/emailchain/lib/actions_smtp_server.go @@ -0,0 +1,37 @@ +package lib + +import ( + wasmx "github.com/loredanacirstea/wasmx-env/lib" +) + +func HandleSmtpLogin(req *LoginRequest) ([]byte, error) { + // Connect to database to check if username exists + err := ConnectSql(ConnectionId) + if err != nil { + wasmx.Revert([]byte("HandleSmtpLogin: DB connection failed: " + err.Error())) + } + + // Validate username exists in database + owners, err := GetAccount(req.Username) + if err != nil { + wasmx.Revert([]byte("HandleSmtpLogin: failed to check account: " + err.Error())) + } + if len(owners) == 0 { + wasmx.Revert([]byte("unauthorized: invalid username")) + } + + // Validate password against stored server password + storedPassword := LoadServerPassword() + if storedPassword == "" { + wasmx.Revert([]byte("unauthorized: server password not configured")) + } + if req.Password != storedPassword { + wasmx.Revert([]byte("unauthorized: invalid password")) + } + + return nil, nil +} + +func HandleSmtpLogout(req *LogoutRequest) ([]byte, error) { + return nil, nil +} diff --git a/tests/testdata/tinygo/emailchain/dns.go b/tests/testdata/tinygo/emailchain/lib/dns.go similarity index 98% rename from tests/testdata/tinygo/emailchain/dns.go rename to tests/testdata/tinygo/emailchain/lib/dns.go index 06c7dfea..bea8a053 100644 --- a/tests/testdata/tinygo/emailchain/dns.go +++ b/tests/testdata/tinygo/emailchain/lib/dns.go @@ -1,4 +1,4 @@ -package main +package lib import ( "encoding/json" @@ -8,7 +8,7 @@ import ( "strings" dnsMox "github.com/loredanacirstea/mailverif/dns" - httpclient "github.com/loredanacirstea/wasmx-env-httpclient" + httpclient "github.com/loredanacirstea/wasmx-env-httpclient/lib" ) type MX struct { diff --git a/tests/testdata/tinygo/emailchain/email.go b/tests/testdata/tinygo/emailchain/lib/email.go similarity index 96% rename from tests/testdata/tinygo/emailchain/email.go rename to tests/testdata/tinygo/emailchain/lib/email.go index ae9539f8..8bf6e8fc 100644 --- a/tests/testdata/tinygo/emailchain/email.go +++ b/tests/testdata/tinygo/emailchain/lib/email.go @@ -1,4 +1,4 @@ -package main +package lib import ( "bytes" @@ -15,8 +15,9 @@ import ( "github.com/loredanacirstea/mailverif/dkim" "github.com/loredanacirstea/mailverif/dns" "github.com/loredanacirstea/mailverif/utils" - vmimap "github.com/loredanacirstea/wasmx-env-imap" - vmsmtp "github.com/loredanacirstea/wasmx-env-smtp" + vmimap "github.com/loredanacirstea/wasmx-env-imap/lib" + vmsmtp "github.com/loredanacirstea/wasmx-env-smtp/lib" + wasmx "github.com/loredanacirstea/wasmx-env/lib" ) // TODO remove me @@ -270,7 +271,7 @@ func sendEmailInternal( mxHost := strings.TrimSuffix(mx.Host, ".") addr := fmt.Sprintf("%s:25", mxHost) - fmt.Println("Trying to send to %s...", addr) + wasmx.LoggerInfo(MODULE_NAME, "trying to send", []string{"to", addr}) tlsConfig := &vmsmtp.TlsConfig{ ServerName: mxHost, @@ -305,7 +306,7 @@ func sendEmailInternal( return fmt.Errorf(sendresp.Error) } vmsmtp.Quit(&vmsmtp.SmtpQuitRequest{Id: mxHost}) - fmt.Println("Email sent successfully to", to) + wasmx.LoggerInfo(MODULE_NAME, "email sent successfully", []string{"to", to}) return nil } return fmt.Errorf("could not deliver email to any MX server for %s", toDomain) diff --git a/tests/testdata/tinygo/emailchain/forward_sign.go b/tests/testdata/tinygo/emailchain/lib/forward_sign.go similarity index 99% rename from tests/testdata/tinygo/emailchain/forward_sign.go rename to tests/testdata/tinygo/emailchain/lib/forward_sign.go index f04aea25..f4e0e864 100644 --- a/tests/testdata/tinygo/emailchain/forward_sign.go +++ b/tests/testdata/tinygo/emailchain/lib/forward_sign.go @@ -1,4 +1,4 @@ -package main +package lib // import ( // "crypto" diff --git a/tests/testdata/tinygo/emailchain/imap/imapnum/types.go b/tests/testdata/tinygo/emailchain/lib/imap/imapnum/types.go similarity index 100% rename from tests/testdata/tinygo/emailchain/imap/imapnum/types.go rename to tests/testdata/tinygo/emailchain/lib/imap/imapnum/types.go diff --git a/tests/testdata/tinygo/emailchain/imap/imapserver/types.go b/tests/testdata/tinygo/emailchain/lib/imap/imapserver/types.go similarity index 100% rename from tests/testdata/tinygo/emailchain/imap/imapserver/types.go rename to tests/testdata/tinygo/emailchain/lib/imap/imapserver/types.go diff --git a/tests/testdata/tinygo/emailchain/imap/types.go b/tests/testdata/tinygo/emailchain/lib/imap/types.go similarity index 99% rename from tests/testdata/tinygo/emailchain/imap/types.go rename to tests/testdata/tinygo/emailchain/lib/imap/types.go index 751a154a..c593924c 100644 --- a/tests/testdata/tinygo/emailchain/imap/types.go +++ b/tests/testdata/tinygo/emailchain/lib/imap/types.go @@ -4,7 +4,7 @@ import ( "time" "unsafe" - "github.com/loredanacirstea/emailchain/imap/imapnum" + "github.com/loredanacirstea/emailchain/lib/imap/imapnum" ) type MailboxAttr string diff --git a/tests/testdata/tinygo/emailchain/lib/kayrosbot.go b/tests/testdata/tinygo/emailchain/lib/kayrosbot.go new file mode 100644 index 00000000..f365203d --- /dev/null +++ b/tests/testdata/tinygo/emailchain/lib/kayrosbot.go @@ -0,0 +1,324 @@ +package lib + +import ( + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "encoding/json" + "fmt" + "net/http" + "strings" + "time" + + "github.com/emersion/go-message/mail" + httpclient "github.com/loredanacirstea/wasmx-env-httpclient/lib" + vmimap "github.com/loredanacirstea/wasmx-env-imap/lib" + wasmx "github.com/loredanacirstea/wasmx-env/lib" +) + +const ( + BotEmail = "kayros1@dmail.provable.dev" + KayrosAPIURL = "https://kayros.provable.dev" + KayrosAPIPOST = KayrosAPIURL + "/api/grpc/single-hash" + KayrosAPIGET = KayrosAPIURL + "/api/database/record-by-hash?hash_item=" + DataTypeEmail = "70726f7661626c655f656d61696c000000000000000000000000000000000000" +) + +// KayrosRequest represents the request to Kayros API +type KayrosRequest struct { + DataItem string `json:"data_item"` + DataType string `json:"data_type"` +} + +// KayrosResponse represents the response from Kayros API +type KayrosResponse struct { + Data interface{} `json:"data,omitempty"` +} + +// KayrosProof represents the JSON file structure attached to emails +type KayrosProof struct { + Data string `json:"data"` + Kayros interface{} `json:"kayros"` +} + +// IsBotEmail checks if the email is addressed to the bot +func IsBotEmail(toAddresses []string) bool { + for _, to := range toAddresses { + if strings.EqualFold(strings.TrimSpace(to), BotEmail) { + return true + } + } + return false +} + +// HashEmail computes SHA256 hash of email raw bytes and returns hex string +func HashEmail(emailRaw []byte) string { + hash := sha256.Sum256(emailRaw) + return hex.EncodeToString(hash[:]) +} + +// QueryKayros makes a POST request to Kayros API with email hash +func QueryKayros(emailHash string) (interface{}, error) { + // Create request body + reqBody := KayrosRequest{ + DataItem: emailHash, + DataType: DataTypeEmail, + } + + reqBodyBytes, err := json.Marshal(reqBody) + if err != nil { + return nil, fmt.Errorf("failed to marshal request: %w", err) + } + + // Create HTTP request + headers := http.Header{} + headers.Set("Content-Type", "application/json") + headers.Set("Accept", "application/json") + + httpReq := httpclient.HttpRequestWrap{ + Request: httpclient.HttpRequest{ + Method: "POST", + Url: KayrosAPIPOST, + Header: headers, + Data: reqBodyBytes, + }, + ResponseHandler: httpclient.ResponseHandler{ + MaxSize: 10 * 1024 * 1024, // 10MB max response + }, + } + + // Make the HTTP request + resp := httpclient.Request(&httpReq) + if resp.Error != "" { + return nil, fmt.Errorf("HTTP request failed: %s", resp.Error) + } + + // Check status code + if resp.Data.StatusCode != 200 { + return nil, fmt.Errorf("Kayros API returned status %d: %s", resp.Data.StatusCode, string(resp.Data.Data)) + } + + // Parse response as generic interface + var kayrosResp interface{} + err = json.Unmarshal(resp.Data.Data, &kayrosResp) + if err != nil { + return nil, fmt.Errorf("failed to parse Kayros response: %w", err) + } + + return kayrosResp, nil +} + +// CreateKayrosProof creates a proof structure combining email data and Kayros response +func CreateKayrosProof(emailRaw []byte, kayrosResp interface{}) *KayrosProof { + return &KayrosProof{ + Data: base64.StdEncoding.EncodeToString(emailRaw), + Kayros: kayrosResp, + } +} + +// GenerateProofFilename creates a timestamped filename for the proof +func GenerateProofFilename(timestamp int64) string { + t := time.Unix(timestamp, 0).UTC() + return fmt.Sprintf("kayros_proof_%s.json", t.Format("20060102_150405")) +} + +// unfoldHeader removes folding whitespace from email headers +// Email headers can be folded across multiple lines using \r\n followed by whitespace +func unfoldHeader(value string) string { + // Replace \r\n followed by whitespace with a single space + value = strings.ReplaceAll(value, "\r\n ", " ") + value = strings.ReplaceAll(value, "\r\n\t", " ") + // Also handle just \n (some implementations are sloppy) + value = strings.ReplaceAll(value, "\n ", " ") + value = strings.ReplaceAll(value, "\n\t", " ") + // Remove any remaining \r\n + value = strings.ReplaceAll(value, "\r\n", " ") + value = strings.ReplaceAll(value, "\n", " ") + value = strings.ReplaceAll(value, "\r", " ") + return strings.TrimSpace(value) +} + +// HandleKayrosBot processes incoming emails to the bot and sends a reply with Kayros proof +func HandleKayrosBot(req *IncomingEmailRequest) error { + wasmx.LoggerInfo(MODULE_NAME, "kayrosbot handler", []string{}) + // Extract subject, Message-ID, and References from email + subject := "Indexed by Kayros" + var messageID, references string + + headerValues, err := extractHeaders(req.EmailRaw, []string{"Subject", "Message-ID", "References"}) + if err == nil && len(headerValues) >= 1 { + if len(headerValues[0]) > 0 { + subject = unfoldHeader(headerValues[0]) + } + if len(headerValues) >= 2 && len(headerValues[1]) > 0 { + messageID = unfoldHeader(headerValues[1]) + } + if len(headerValues) >= 3 && len(headerValues[2]) > 0 { + references = unfoldHeader(headerValues[2]) + } + } + + // Hash the email + emailHash := HashEmail(req.EmailRaw) + wasmx.LoggerInfo(MODULE_NAME, "processing email", []string{"from", req.From[0], "hash", emailHash}) + + // Query Kayros API + kayrosResp, err := QueryKayros(emailHash) + if err != nil { + wasmx.LoggerError(MODULE_NAME, "Kayros API error", []string{"error", err.Error()}) + return fmt.Errorf("kayros API query failed: %w", err) + } + + // Extract computed_hash_hex from Kayros response + computedHash := "" + if kayrosData, ok := kayrosResp.(map[string]interface{}); ok { + if data, ok := kayrosData["data"].(map[string]interface{}); ok { + if hash, ok := data["computed_hash_hex"].(string); ok { + computedHash = hash + } + } + } + + // Create proof structure + proof := CreateKayrosProof(req.EmailRaw, kayrosResp) + + // Marshal proof to JSON + proofJSON, err := json.MarshalIndent(proof, "", " ") + if err != nil { + return fmt.Errorf("failed to marshal proof: %w", err) + } + + // Generate filename + filename := GenerateProofFilename(req.Timestamp) + + // Build reply email with attachment + err = SendKayrosBotReply(req.From, filename, proofJSON, req.Timestamp, subject, messageID, references, computedHash) + if err != nil { + return fmt.Errorf("failed to send reply: %w", err) + } + wasmx.LoggerInfo(MODULE_NAME, "sent reply to", []string{"from", req.From[0], "hash", emailHash}) + return nil +} + +// SendKayrosBotReply sends an email reply with the Kayros proof JSON attached +func SendKayrosBotReply(toAddresses []string, filename string, proofJSON []byte, timestamp int64, subject string, messageID string, references string, computedHash string) error { + // Load DKIM signing options + opts := LoadDkimKey() + if opts == nil { + return fmt.Errorf("no DKIM keys configured") + } + + // Connect to database + err := ConnectSql(ConnectionId) + if err != nil { + return fmt.Errorf("database connection failed: %w", err) + } + + // Build email envelope + from := vmimap.AddressFromString(BotEmail, "Kayros Indexer from provable.dev") + to := vmimap.AddressesFromString(toAddresses) + + date := time.Unix(timestamp, 0).UTC() + + envelope := &vmimap.Envelope{ + Subject: "Fwd: " + subject, + From: []vmimap.Address{from}, + To: to, + Date: date, + InReplyTo: []string{messageID}, + } + + // Build headers + hdr := mail.Header{} + + // Build References header: original References + original Message-ID + var replyReferences []string + if references != "" { + replyReferences = append(replyReferences, references) + } + if messageID != "" { + replyReferences = append(replyReferences, messageID) + } + if len(replyReferences) > 0 { + hdr.Set("References", strings.Join(replyReferences, " ")) + } + + headers, err := SerializeEnvelope2(envelope, hdr) + if err != nil { + return fmt.Errorf("failed to serialize envelope: %w", err) + } + + // Email body text + var bodyText string + if computedHash != "" { + bodyText = fmt.Sprintf(`Indexed by Kayros. See attached %s + +View stored record on Kayros: %s%s +`, filename, KayrosAPIGET, computedHash) + } else { + bodyText = fmt.Sprintf(`Failure to create proof. See attached %s for details. +`, filename) + } + + // Create email with attachment + // Use multipart/mixed for attachment + boundary := fmt.Sprintf("boundary_%d", timestamp) + email := vmimap.Email{ + Headers: headers, + Body: vmimap.EmailBody{ + ContentType: fmt.Sprintf("multipart/mixed; boundary=\"%s\"", boundary), + Boundary: boundary, + Parts: []vmimap.BodyPart{ + { + ContentType: "text/plain; charset=UTF-8", + Body: []byte(bodyText), + }, + }, + }, + Attachments: []vmimap.Attachment{ + { + Filename: filename, + ContentType: "application/json", + Data: proofJSON, + }, + }, + } + + // Build raw email + emailStr, err := BuildRawEmail2(email) + if err != nil { + return fmt.Errorf("failed to build email: %w", err) + } + + // Prepare email for sending (add Message-ID and DKIM signature) + prepped, err := prepareEmailSend(*opts, emailStr, BotEmail, date, true) + if err != nil { + return fmt.Errorf("failed to prepare email: %w", err) + } + + // Send email to each recipient + errs := []string{} + for _, to := range toAddresses { + err = sendEmailInternal(BotEmail, to, prepped, MailServerDomain, DefaultNetworkType) + if err != nil { + errs = append(errs, fmt.Sprintf("%s: %v", to, err)) + wasmx.LoggerError(MODULE_NAME, "failed to send", []string{"to", to, "error", err.Error()}) + continue + } + wasmx.LoggerInfo(MODULE_NAME, "sent email to", []string{"to", to}) + } + + // Store sent email in bot's SENT folder (unless disabled) + if !LoadKayrosBotNoStore() { + err = StoreEmail(BotEmail, []string{}, []byte(prepped), "", ConnectionId, FolderSent) + if err != nil { + errs = append(errs, fmt.Sprintf("store error: %v", err)) + } + } + + if len(errs) > 0 { + return fmt.Errorf("send errors: %s", strings.Join(errs, "; ")) + } + + return nil +} diff --git a/tests/testdata/tinygo/emailchain/storage.go b/tests/testdata/tinygo/emailchain/lib/storage.go similarity index 96% rename from tests/testdata/tinygo/emailchain/storage.go rename to tests/testdata/tinygo/emailchain/lib/storage.go index 181aaf93..3d5c17a6 100644 --- a/tests/testdata/tinygo/emailchain/storage.go +++ b/tests/testdata/tinygo/emailchain/lib/storage.go @@ -1,4 +1,4 @@ -package main +package lib import ( "bufio" @@ -12,8 +12,8 @@ import ( "github.com/loredanacirstea/mailverif/dkim" "github.com/loredanacirstea/mailverif/utils" - imap "github.com/loredanacirstea/wasmx-env-imap" - sql "github.com/loredanacirstea/wasmx-env-sql" + imap "github.com/loredanacirstea/wasmx-env-imap/lib" + sql "github.com/loredanacirstea/wasmx-env-sql/lib" wasmx "github.com/loredanacirstea/wasmx-env/lib" ) @@ -332,19 +332,15 @@ func extractEmail(raw []byte) (*Email, error) { envelope.Bcc = v case imap.HEADER_LOW_IN_REPLY_TO: v := h.GetValueTrimmed() - fmt.Println("--HEADER_LOW_IN_REPLY_TO--", v) inReplyTo := strings.Trim(v, "<>") envelope.InReplyTo = []string{inReplyTo} - fmt.Println("--envelope.InReplyTo--", envelope.InReplyTo) case imap.HEADER_LOW_REPLY_TO: valuestr := h.GetValueTrimmed() - fmt.Println("--HEADER_LOW_REPLY_TO--", valuestr) v, err := imap.ParseEmailAddresses(valuestr) if err != nil { return nil, err } envelope.ReplyTo = v - fmt.Println("--envelope.ReplyTo--", envelope.ReplyTo) default: continue } diff --git a/tests/testdata/tinygo/emailchain/lib/storage_simple.go b/tests/testdata/tinygo/emailchain/lib/storage_simple.go new file mode 100644 index 00000000..1b7ac116 --- /dev/null +++ b/tests/testdata/tinygo/emailchain/lib/storage_simple.go @@ -0,0 +1,51 @@ +package lib + +import ( + "encoding/json" + + wasmx "github.com/loredanacirstea/wasmx-env/lib" +) + +func StoreDkimKey(opts SignOptions) error { + optsbz, err := json.Marshal(opts) + if err != nil { + return err + } + wasmx.StorageStore([]byte(`dkim_keys`), optsbz) + return nil +} + +func LoadDkimKey() *SignOptions { + v := &SignOptions{} + bz := wasmx.StorageLoad([]byte(`dkim_keys`)) + err := json.Unmarshal(bz, v) + if err != nil { + return nil + } + return v +} + +func StoreServerPassword(password string) { + wasmx.StorageStore([]byte(`server_password`), []byte(password)) +} + +func LoadServerPassword() string { + bz := wasmx.StorageLoad([]byte(`server_password`)) + return string(bz) +} + +func StoreKayrosBotNoStore(noStore bool) { + var val byte + if noStore { + val = 1 + } + wasmx.StorageStore([]byte(`kayrosbot_no_store`), []byte{val}) +} + +func LoadKayrosBotNoStore() bool { + bz := wasmx.StorageLoad([]byte(`kayrosbot_no_store`)) + if len(bz) == 0 { + return false + } + return bz[0] == 1 +} diff --git a/tests/testdata/tinygo/emailchain/types.go b/tests/testdata/tinygo/emailchain/lib/types.go similarity index 94% rename from tests/testdata/tinygo/emailchain/types.go rename to tests/testdata/tinygo/emailchain/lib/types.go index dc95f1a6..545f5622 100644 --- a/tests/testdata/tinygo/emailchain/types.go +++ b/tests/testdata/tinygo/emailchain/lib/types.go @@ -1,4 +1,4 @@ -package main +package lib import ( "encoding/json" @@ -8,15 +8,17 @@ import ( "golang.org/x/crypto/ed25519" - "github.com/loredanacirstea/wasmx-env" - imap "github.com/loredanacirstea/wasmx-env-imap" - vmimap "github.com/loredanacirstea/wasmx-env-imap" - vmsmtp "github.com/loredanacirstea/wasmx-env-smtp" + imap "github.com/loredanacirstea/wasmx-env-imap/lib" + vmimap "github.com/loredanacirstea/wasmx-env-imap/lib" + vmsmtp "github.com/loredanacirstea/wasmx-env-smtp/lib" + wasmx "github.com/loredanacirstea/wasmx-env/lib" "github.com/loredanacirstea/mailverif/arc" "github.com/loredanacirstea/mailverif/dkim" ) +const MODULE_NAME = "emailchain" + const ConnectionId = "emailchain" type Calldata struct { @@ -46,12 +48,13 @@ type ReentryCalldata struct { } type IncomingEmailRequest struct { - ConnectionId string - IpFrom string `json:"ipfrom"` - From []string `json:"from"` - To []string `json:"to"` - EmailRaw []byte `json:"email_raw"` - Timestamp int64 `json:"timestamp"` + ConnectionId string + IpFrom string `json:"ipfrom"` + From []string `json:"from"` + To []string `json:"to"` + EmailRaw []byte `json:"email_raw"` + Timestamp int64 `json:"timestamp"` + AuthenticatedUser string `json:"authenticated_user"` // Username from SMTP authentication } type ConnectRequest struct { @@ -61,9 +64,11 @@ type ConnectRequest struct { } type StartServerRequest struct { - SignOptions SignOptions `json:"options"` - Smtp vmsmtp.ServerConfig `json:"smtp"` - Imap vmimap.ServerConfig `json:"imap"` + SignOptions SignOptions `json:"options"` + Smtp vmsmtp.ServerConfig `json:"smtp"` + Imap vmimap.ServerConfig `json:"imap"` + Password string `json:"password"` + KayrosBotNoStore bool `json:"kayrosbot_no_store"` // If true, don't store emails sent by Kayros bot } type CloseRequest struct { diff --git a/tests/testdata/tinygo/emailchain/types_imap_server.go b/tests/testdata/tinygo/emailchain/lib/types_imap_server.go similarity index 95% rename from tests/testdata/tinygo/emailchain/types_imap_server.go rename to tests/testdata/tinygo/emailchain/lib/types_imap_server.go index cc082b11..17bb3112 100644 --- a/tests/testdata/tinygo/emailchain/types_imap_server.go +++ b/tests/testdata/tinygo/emailchain/lib/types_imap_server.go @@ -1,8 +1,8 @@ -package main +package lib import ( - imap "github.com/loredanacirstea/emailchain/imap" - imapserver "github.com/loredanacirstea/emailchain/imap/imapserver" + imap "github.com/loredanacirstea/emailchain/lib/imap" + imapserver "github.com/loredanacirstea/emailchain/lib/imap/imapserver" ) type ReentryCalldataServer struct { diff --git a/tests/testdata/tinygo/emailchain/types_storage.go b/tests/testdata/tinygo/emailchain/lib/types_storage.go similarity index 95% rename from tests/testdata/tinygo/emailchain/types_storage.go rename to tests/testdata/tinygo/emailchain/lib/types_storage.go index 32dda58c..e7f58ee1 100644 --- a/tests/testdata/tinygo/emailchain/types_storage.go +++ b/tests/testdata/tinygo/emailchain/lib/types_storage.go @@ -1,4 +1,4 @@ -package main +package lib type FolderState struct { Owner string `json:"owner"` diff --git a/tests/testdata/tinygo/emailchain/utils.go b/tests/testdata/tinygo/emailchain/lib/utils.go similarity index 97% rename from tests/testdata/tinygo/emailchain/utils.go rename to tests/testdata/tinygo/emailchain/lib/utils.go index 11dca32a..2e7fc579 100644 --- a/tests/testdata/tinygo/emailchain/utils.go +++ b/tests/testdata/tinygo/emailchain/lib/utils.go @@ -1,4 +1,4 @@ -package main +package lib import ( "bufio" @@ -11,7 +11,7 @@ import ( "strings" "time" - "github.com/loredanacirstea/emailchain/imap" + "github.com/loredanacirstea/emailchain/lib/imap" "github.com/loredanacirstea/mailverif/dkim" "github.com/loredanacirstea/mailverif/utils" ) diff --git a/tests/testdata/tinygo/emailchain/storage_simple.go b/tests/testdata/tinygo/emailchain/storage_simple.go deleted file mode 100644 index 3968a934..00000000 --- a/tests/testdata/tinygo/emailchain/storage_simple.go +++ /dev/null @@ -1,26 +0,0 @@ -package main - -import ( - "encoding/json" - - wasmx "github.com/loredanacirstea/wasmx-env/lib" -) - -func StoreDkimKey(opts SignOptions) error { - optsbz, err := json.Marshal(opts) - if err != nil { - return err - } - wasmx.StorageStore([]byte(`dkim_keys`), optsbz) - return nil -} - -func LoadDkimKey() *SignOptions { - v := &SignOptions{} - bz := wasmx.StorageLoad([]byte(`dkim_keys`)) - err := json.Unmarshal(bz, v) - if err != nil { - return nil - } - return v -} diff --git a/tests/testdata/tinygo/wasmx-env-httpclient/cmd/main.go b/tests/testdata/tinygo/wasmx-env-httpclient/cmd/main.go new file mode 100644 index 00000000..3a6cf272 --- /dev/null +++ b/tests/testdata/tinygo/wasmx-env-httpclient/cmd/main.go @@ -0,0 +1,15 @@ +package main + +//go:wasm-module httpclient +//export wasmx_httpclient_i64_1 +func Wasmx_httpclient_i64_1() {} + +//go:wasm-module wasmx +//export memory_ptrlen_i64_1 +func Wemory_ptrlen_i64_1() {} + +//go:wasm-module wasmx-env-smtp +//export instantiate +func Instantiate() {} + +func main() {} diff --git a/tests/testdata/tinygo/wasmx-env-httpclient/httpclient.go b/tests/testdata/tinygo/wasmx-env-httpclient/lib/httpclient.go similarity index 85% rename from tests/testdata/tinygo/wasmx-env-httpclient/httpclient.go rename to tests/testdata/tinygo/wasmx-env-httpclient/lib/httpclient.go index 9df2b362..f55e0dd8 100644 --- a/tests/testdata/tinygo/wasmx-env-httpclient/httpclient.go +++ b/tests/testdata/tinygo/wasmx-env-httpclient/lib/httpclient.go @@ -9,10 +9,6 @@ import ( utils "github.com/loredanacirstea/wasmx-env-utils" ) -//go:wasm-module httpclient -//export wasmx_httpclient_i64_1 -func wasmx_httpclient_i64_1() {} - //go:wasmimport httpclient Request func Request_(reqPtr int64) int64 diff --git a/tests/testdata/tinygo/wasmx-env-httpclient/types.go b/tests/testdata/tinygo/wasmx-env-httpclient/lib/types.go similarity index 100% rename from tests/testdata/tinygo/wasmx-env-httpclient/types.go rename to tests/testdata/tinygo/wasmx-env-httpclient/lib/types.go diff --git a/tests/testdata/tinygo/wasmx-env-imap/cmd/main.go b/tests/testdata/tinygo/wasmx-env-imap/cmd/main.go new file mode 100644 index 00000000..191b6be3 --- /dev/null +++ b/tests/testdata/tinygo/wasmx-env-imap/cmd/main.go @@ -0,0 +1,15 @@ +package main + +//go:wasm-module imap +//export wasmx_imap_i64_1 +func Wasmx_imap_i64_1() {} + +//go:wasm-module wasmx +//export memory_ptrlen_i64_1 +func Wemory_ptrlen_i64_1() {} + +//go:wasm-module wasmx-env-imap +//export instantiate +func Instantiate() {} + +func main() {} diff --git a/tests/testdata/tinygo/wasmx-env-imap/errors.go b/tests/testdata/tinygo/wasmx-env-imap/lib/errors.go similarity index 100% rename from tests/testdata/tinygo/wasmx-env-imap/errors.go rename to tests/testdata/tinygo/wasmx-env-imap/lib/errors.go diff --git a/tests/testdata/tinygo/wasmx-env-imap/imap.go b/tests/testdata/tinygo/wasmx-env-imap/lib/imap.go similarity index 98% rename from tests/testdata/tinygo/wasmx-env-imap/imap.go rename to tests/testdata/tinygo/wasmx-env-imap/lib/imap.go index d4f2fdda..0fc08364 100644 --- a/tests/testdata/tinygo/wasmx-env-imap/imap.go +++ b/tests/testdata/tinygo/wasmx-env-imap/lib/imap.go @@ -9,10 +9,6 @@ import ( utils "github.com/loredanacirstea/wasmx-env-utils" ) -//go:wasm-module imap -//export wasmx_imap_i64_1 -func wasmx_imap_i64_1() {} - //go:wasmimport imap Connect func Connect_(reqPtr int64) int64 diff --git a/tests/testdata/tinygo/wasmx-env-imap/response.go b/tests/testdata/tinygo/wasmx-env-imap/lib/response.go similarity index 100% rename from tests/testdata/tinygo/wasmx-env-imap/response.go rename to tests/testdata/tinygo/wasmx-env-imap/lib/response.go diff --git a/tests/testdata/tinygo/wasmx-env-imap/types.go b/tests/testdata/tinygo/wasmx-env-imap/lib/types.go similarity index 100% rename from tests/testdata/tinygo/wasmx-env-imap/types.go rename to tests/testdata/tinygo/wasmx-env-imap/lib/types.go diff --git a/tests/testdata/tinygo/wasmx-env-imap/types_imap.go b/tests/testdata/tinygo/wasmx-env-imap/lib/types_imap.go similarity index 100% rename from tests/testdata/tinygo/wasmx-env-imap/types_imap.go rename to tests/testdata/tinygo/wasmx-env-imap/lib/types_imap.go diff --git a/tests/testdata/tinygo/wasmx-env-smtp/cmd/main.go b/tests/testdata/tinygo/wasmx-env-smtp/cmd/main.go new file mode 100644 index 00000000..8e8df0e0 --- /dev/null +++ b/tests/testdata/tinygo/wasmx-env-smtp/cmd/main.go @@ -0,0 +1,15 @@ +package main + +//go:wasm-module smtp +//export wasmx_smtp_i64_1 +func Wasmx_smtp_i64_1() {} + +//go:wasm-module wasmx +//export memory_ptrlen_i64_1 +func Wemory_ptrlen_i64_1() {} + +//go:wasm-module wasmx-env-smtp +//export instantiate +func Instantiate() {} + +func main() {} diff --git a/tests/testdata/tinygo/wasmx-env-smtp/smtp.go b/tests/testdata/tinygo/wasmx-env-smtp/lib/smtp.go similarity index 98% rename from tests/testdata/tinygo/wasmx-env-smtp/smtp.go rename to tests/testdata/tinygo/wasmx-env-smtp/lib/smtp.go index a7630311..b88ec231 100644 --- a/tests/testdata/tinygo/wasmx-env-smtp/smtp.go +++ b/tests/testdata/tinygo/wasmx-env-smtp/lib/smtp.go @@ -9,10 +9,6 @@ import ( utils "github.com/loredanacirstea/wasmx-env-utils" ) -//go:wasm-module smtp -//export wasmx_smtp_i64_1 -func wasmx_smtp_i64_1() {} - //go:wasmimport smtp ClientConnect func ClientConnect_(reqPtr int64) int64 diff --git a/tests/testdata/tinygo/wasmx-env-smtp/types.go b/tests/testdata/tinygo/wasmx-env-smtp/lib/types.go similarity index 98% rename from tests/testdata/tinygo/wasmx-env-smtp/types.go rename to tests/testdata/tinygo/wasmx-env-smtp/lib/types.go index ccb7379d..6e781be6 100644 --- a/tests/testdata/tinygo/wasmx-env-smtp/types.go +++ b/tests/testdata/tinygo/wasmx-env-smtp/lib/types.go @@ -3,7 +3,7 @@ package smtp import ( "time" - vmimap "github.com/loredanacirstea/wasmx-env-imap" + vmimap "github.com/loredanacirstea/wasmx-env-imap/lib" ) type TlsConfig struct { diff --git a/tests/testdata/tinygo/wasmx-env-sql/cmd/main.go b/tests/testdata/tinygo/wasmx-env-sql/cmd/main.go new file mode 100644 index 00000000..87c6d46e --- /dev/null +++ b/tests/testdata/tinygo/wasmx-env-sql/cmd/main.go @@ -0,0 +1,15 @@ +package main + +//go:wasm-module sql +//export wasmx_sql_i64_1 +func Wasmx_sql_i64_1() {} + +//go:wasm-module wasmx +//export memory_ptrlen_i64_1 +func Wemory_ptrlen_i64_1() {} + +//go:wasm-module wasmx-env-sql +//export instantiate +func Instantiate() {} + +func main() {} diff --git a/tests/testdata/tinygo/wasmx-env-sql/sql.go b/tests/testdata/tinygo/wasmx-env-sql/lib/sql.go similarity index 97% rename from tests/testdata/tinygo/wasmx-env-sql/sql.go rename to tests/testdata/tinygo/wasmx-env-sql/lib/sql.go index cd8a8785..647e12a7 100644 --- a/tests/testdata/tinygo/wasmx-env-sql/sql.go +++ b/tests/testdata/tinygo/wasmx-env-sql/lib/sql.go @@ -9,10 +9,6 @@ import ( utils "github.com/loredanacirstea/wasmx-env-utils" ) -//go:wasm-module sql -//export wasmx_sql_i64_1 -func wasmx_sql_i64_1() {} - // Host function imports // //go:wasmimport sql Connect diff --git a/tests/testdata/tinygo/wasmx-env-sql/types.go b/tests/testdata/tinygo/wasmx-env-sql/lib/types.go similarity index 100% rename from tests/testdata/tinygo/wasmx-env-sql/types.go rename to tests/testdata/tinygo/wasmx-env-sql/lib/types.go diff --git a/tests/vmemail/kayrosbot_test.go b/tests/vmemail/kayrosbot_test.go new file mode 100644 index 00000000..f11c6f8d --- /dev/null +++ b/tests/vmemail/kayrosbot_test.go @@ -0,0 +1,97 @@ +package keeper_test + +import ( + _ "embed" + "encoding/json" + "fmt" + "os" + "os/signal" + "syscall" + + sdkmath "cosmossdk.io/math" + sdk "github.com/cosmos/cosmos-sdk/types" + + vmimap "github.com/loredanacirstea/wasmx-vmimap" + vmsmtp "github.com/loredanacirstea/wasmx-vmsmtp" + "github.com/loredanacirstea/wasmx/x/wasmx/types" + + "github.com/loredanacirstea/mythos-tests/vmsql/utils" + ut "github.com/loredanacirstea/wasmx/testutil/wasmx" +) + +func (suite *KeeperTestSuite) TestEmailKayrosBot() { + if !suite.runEmailServer { + suite.T().Skipf("Skipping email server test: TestEmailKayrosBot") + } + sender := suite.GetRandomAccount() + initBalance := sdkmath.NewInt(ut.DEFAULT_BALANCE).MulRaw(5000) + + appA := s.AppContext() + appA.Faucet.Fund(appA.Context(), appA.BytesToAccAddressPrefixed(sender.Address), sdk.NewCoin(appA.Chain.Config.BaseDenom, initBalance)) + + contractAddress := appA.BytesToAccAddressPrefixed(types.AccAddressFromHex(types.ADDR_EMAIL_HANDLER)) + + // set a role to have access to protected APIs + utils.RegisterRole(suite, appA, "emailprover", contractAddress, sender) + + // tlsPath := "~/dev/letsencrypt/" + tlsPath := "/etc/letsencrypt/live/" + + // Prepare the VerifyDKIM request + msg := &EmailChainCalldata{ + StartServer: &StartServerRequest{ + SignOptions: SignOptions{ + Domain: "dmail.provable.dev", + Selector: "2025a", + PrivateKeyType: "rsa", + PrivateKey: []byte(testPrivateKeyPEM), + Identifier: "", + }, + Smtp: vmsmtp.ServerConfig{ + Network: "tcp4", + Domain: "dmail.provable.dev", + TlsConfig: &vmsmtp.TlsConfig{ + TLSCertFile: tlsPath + "dmail.provable.dev/fullchain.pem", + TLSKeyFile: tlsPath + "dmail.provable.dev/privkey.pem", + ServerName: "dmail.provable.dev", + }, + }, + Imap: vmimap.ServerConfig{ + TlsConfig: &vmimap.TlsConfig{ + TLSCertFile: tlsPath + "dmail.provable.dev/fullchain.pem", + TLSKeyFile: tlsPath + "dmail.provable.dev/privkey.pem", + ServerName: "dmail.provable.dev", + }, + Network: "tcp4", + }, + }, + } + data, err := json.Marshal(msg) + fmt.Println(string(data)) + suite.Require().NoError(err) + appA.ExecuteContractWithGas(sender, contractAddress, types.WasmxExecutionMessage{Data: data}, nil, nil, 280000000, nil) + fmt.Println("started email server") + + // create test account1 + msg = &EmailChainCalldata{ + CreateAccount: &CreateAccountRequest{ + Username: "bot@dmail.provable.dev", + Password: "123456", + }, + } + data, err = json.Marshal(msg) + fmt.Println(string(data)) + suite.Require().NoError(err) + appA.ExecuteContractWithGas(sender, contractAddress, types.WasmxExecutionMessage{Data: data}, nil, nil, 280000000, nil) + + suite.T().Log("Running websrv... Press Ctrl+C to exit") + + // Create a channel to listen for interrupt/terminate signals + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGTERM) + + // Block until a signal is received + <-sig + + suite.T().Log("Received exit signal. Test ending.") +} diff --git a/tests/vmemail/smtp_server_test.go b/tests/vmemail/smtp_server_test.go index 4382fd92..27474143 100644 --- a/tests/vmemail/smtp_server_test.go +++ b/tests/vmemail/smtp_server_test.go @@ -18,10 +18,10 @@ import ( vmimap "github.com/loredanacirstea/wasmx-vmimap" vmsmtp "github.com/loredanacirstea/wasmx-vmsmtp" "github.com/loredanacirstea/wasmx/x/wasmx/types" + "github.com/loredanacirstea/wasmx/x/wasmx/vm/precompiles" imap "github.com/emersion/go-imap/v2" - tinygo "github.com/loredanacirstea/mythos-tests/testdata/tinygo" "github.com/loredanacirstea/mythos-tests/vmemail/testdata" "github.com/loredanacirstea/mythos-tests/vmsql/utils" ut "github.com/loredanacirstea/wasmx/testutil/wasmx" @@ -31,13 +31,14 @@ func (suite *KeeperTestSuite) TestIncomingEmail() { defer os.Remove("emailchain.db") defer os.Remove("emailchain.db-shm") defer os.Remove("emailchain.db-wal") - wasmbin := tinygo.EmailChain sender := suite.GetRandomAccount() initBalance := sdkmath.NewInt(ut.DEFAULT_BALANCE).MulRaw(5000) appA := s.AppContext() appA.Faucet.Fund(appA.Context(), appA.BytesToAccAddressPrefixed(sender.Address), sdk.NewCoin(appA.Chain.Config.BaseDenom, initBalance)) + wasmbin := precompiles.GetPrecompileByLabel(appA.AddressCodec(), types.EMAIL_v001) + // Store the emailchain contract and instantiate it codeId := appA.StoreCode(sender, wasmbin, nil) contractAddress := appA.InstantiateCode(sender, codeId, types.WasmxExecutionMessage{Data: []byte{}}, "emailchain", nil) @@ -64,13 +65,14 @@ func (suite *KeeperTestSuite) TestEmailSmtpServer() { if !suite.runEmailServer { suite.T().Skipf("Skipping email server test: TestEmailSmtpServer") } - wasmbin := tinygo.EmailChain sender := suite.GetRandomAccount() initBalance := sdkmath.NewInt(ut.DEFAULT_BALANCE).MulRaw(5000) appA := s.AppContext() appA.Faucet.Fund(appA.Context(), appA.BytesToAccAddressPrefixed(sender.Address), sdk.NewCoin(appA.Chain.Config.BaseDenom, initBalance)) + wasmbin := precompiles.GetPrecompileByLabel(appA.AddressCodec(), types.EMAIL_v001) + // Store the emailchain contract and instantiate it codeId := appA.StoreCode(sender, wasmbin, nil) contractAddress := appA.InstantiateCode(sender, codeId, types.WasmxExecutionMessage{Data: []byte{}}, "emailchain", nil) diff --git a/tests/vmemail/tinygo_forward_test.go b/tests/vmemail/tinygo_forward_test.go index 36b10d1f..a52f4d32 100644 --- a/tests/vmemail/tinygo_forward_test.go +++ b/tests/vmemail/tinygo_forward_test.go @@ -22,8 +22,8 @@ import ( vmimap "github.com/loredanacirstea/wasmx-vmimap" "github.com/loredanacirstea/wasmx/x/wasmx/types" + "github.com/loredanacirstea/wasmx/x/wasmx/vm/precompiles" - tinygo "github.com/loredanacirstea/mythos-tests/testdata/tinygo" testdata "github.com/loredanacirstea/mythos-tests/vmemail/testdata" "github.com/loredanacirstea/mythos-tests/vmsql/utils" ut "github.com/loredanacirstea/wasmx/testutil/wasmx" @@ -164,13 +164,14 @@ func TestEmailTinyGoVerifyDKIM(t *testing.T) { func (suite *KeeperTestSuite) TestEmailTinyGoForwardCustom() { SkipNoPasswordTests(suite.T(), "TestEmailTinyGoForwardCustom") - wasmbin := tinygo.EmailChain sender := suite.GetRandomAccount() initBalance := sdkmath.NewInt(ut.DEFAULT_BALANCE).MulRaw(5000) appA := s.AppContext() appA.Faucet.Fund(appA.Context(), appA.BytesToAccAddressPrefixed(sender.Address), sdk.NewCoin(appA.Chain.Config.BaseDenom, initBalance)) + wasmbin := precompiles.GetPrecompileByLabel(appA.AddressCodec(), types.EMAIL_v001) + // Store the emailchain contract and instantiate it codeId := appA.StoreCode(sender, wasmbin, nil) contractAddress := appA.InstantiateCode(sender, codeId, types.WasmxExecutionMessage{Data: []byte{}}, "emailchain", nil) diff --git a/tests/vmemail/tinygo_test.go b/tests/vmemail/tinygo_test.go index 3c9c8c22..e260b7f3 100644 --- a/tests/vmemail/tinygo_test.go +++ b/tests/vmemail/tinygo_test.go @@ -19,6 +19,7 @@ import ( vmimap "github.com/loredanacirstea/wasmx-vmimap" vmsmtp "github.com/loredanacirstea/wasmx-vmsmtp" "github.com/loredanacirstea/wasmx/x/wasmx/types" + "github.com/loredanacirstea/wasmx/x/wasmx/vm/precompiles" tinygo "github.com/loredanacirstea/mythos-tests/testdata/tinygo" testdata "github.com/loredanacirstea/mythos-tests/vmemail/testdata" @@ -527,13 +528,14 @@ func ARCSignAndVerify(t *testing.T, options *SignOptions, emailStr string, mailf func (suite *KeeperTestSuite) TestEmailTinyGoDKIM() { SkipFixmeTests(suite.T(), "TestEmailTinyGoDKIM") - wasmbin := tinygo.EmailChain sender := suite.GetRandomAccount() initBalance := sdkmath.NewInt(ut.DEFAULT_BALANCE).MulRaw(5000) appA := s.AppContext() appA.Faucet.Fund(appA.Context(), appA.BytesToAccAddressPrefixed(sender.Address), sdk.NewCoin(appA.Chain.Config.BaseDenom, initBalance)) + wasmbin := precompiles.GetPrecompileByLabel(appA.AddressCodec(), types.EMAIL_v001) + // Store the emailchain contract and instantiate it codeId := appA.StoreCode(sender, wasmbin, nil) contractAddress := appA.InstantiateCode(sender, codeId, types.WasmxExecutionMessage{Data: []byte{}}, "emailchain", nil) diff --git a/wasmx-vmimap/ops.go b/wasmx-vmimap/ops.go index 399329e7..041a7d7c 100644 --- a/wasmx-vmimap/ops.go +++ b/wasmx-vmimap/ops.go @@ -89,7 +89,7 @@ func connectCommon( case <-closedChannel: // when close signal is received from Close() API // database is already closed, so we exit this goroutine - ctx.Ctx.Logger().Info(fmt.Sprintf("database connection closed: %s", connId)) + ctx.Ctx.Logger().Info(fmt.Sprintf("imap connection closed: %s", connId)) return nil } }) diff --git a/wasmx-vmpostgresql/ops.go b/wasmx-vmpostgresql/ops.go index ec6bf25e..339688f1 100644 --- a/wasmx-vmpostgresql/ops.go +++ b/wasmx-vmpostgresql/ops.go @@ -66,7 +66,7 @@ func Connect(_context interface{}, rnh memc.RuntimeHandler, params []interface{} case <-closedChannel: // when close signal is received from Close() API // database is already closed, so we exit this goroutine - ctx.Ctx.Logger().Info(fmt.Sprintf("database connection closed: %s", connId)) + ctx.Ctx.Logger().Info(fmt.Sprintf("postgresql database connection closed: %s", connId)) return nil } }) diff --git a/wasmx-vmsmtp/ops.go b/wasmx-vmsmtp/ops.go index f4398985..ace40d2c 100644 --- a/wasmx-vmsmtp/ops.go +++ b/wasmx-vmsmtp/ops.go @@ -90,7 +90,7 @@ func connectCommon( case <-closedChannel: // when close signal is received from Close() API // database is already closed, so we exit this goroutine - ctx.Ctx.Logger().Info(fmt.Sprintf("database connection closed: %s", connId)) + ctx.Ctx.Logger().Info(fmt.Sprintf("smtp connection closed: %s", connId)) return nil } }) @@ -415,7 +415,6 @@ func SendMail(_context interface{}, rnh memc.RuntimeHandler, params []interface{ msgreader := strings.NewReader(string(req.Email)) fmt.Println("--SendMail from,to--", req.From, req.To) - err = conn.Client.SendMail(req.From, req.To, msgreader) if err != nil { response.Error = err.Error() diff --git a/wasmx-vmsmtp/server.go b/wasmx-vmsmtp/server.go index 55640f13..87a75364 100644 --- a/wasmx-vmsmtp/server.go +++ b/wasmx-vmsmtp/server.go @@ -24,7 +24,7 @@ type backend struct { func (b *backend) NewSession(conn *smtp.Conn) (smtp.Session, error) { // TODO implement reject policy by contract // conn.Reject() - fmt.Println("smtpbackend.NewSession", conn.Hostname(), conn.Server().Addr, conn.Server().Network) + // fmt.Println("smtpbackend.NewSession", conn.Hostname(), conn.Server().Addr, conn.Server().Network) c := conn.Conn() // fmt.Println("--smtpbackend.NewSession Conn.LocalAddr--", c.LocalAddr(), c.LocalAddr().Network(), c.LocalAddr().String()) @@ -58,18 +58,21 @@ func (b *backend) NewSession(conn *smtp.Conn) (smtp.Session, error) { } type Session struct { - ConnectionId string - IpFrom string `json:"ipfrom"` - From []string `json:"from"` - To []string `json:"to"` - EmailRaw []byte `json:"email_raw"` - Timestamp int64 `json:"timestamp"` - ctx *Context - conn *smtp.Conn + ConnectionId string + IpFrom string `json:"ipfrom"` + From []string `json:"from"` + To []string `json:"to"` + EmailRaw []byte `json:"email_raw"` + Timestamp int64 `json:"timestamp"` + AuthenticatedUser string `json:"authenticated_user"` // Username from authentication (empty if unauthenticated) + ctx *Context + conn *smtp.Conn } type AuthSession struct { Session + authenticated bool + username string } func (s *Session) Mail(from string, opts *smtp.MailOptions) error { @@ -98,6 +101,30 @@ func (s *Session) Logout() error { } // support AuthSession +// Override Mail to require authentication +func (s *AuthSession) Mail(from string, opts *smtp.MailOptions) error { + if !s.authenticated { + return smtp.ErrAuthRequired + } + return s.Session.Mail(from, opts) +} + +// Override Rcpt to require authentication +func (s *AuthSession) Rcpt(to string, opts *smtp.RcptOptions) error { + if !s.authenticated { + return smtp.ErrAuthRequired + } + return s.Session.Rcpt(to, opts) +} + +// Override Data to require authentication +func (s *AuthSession) Data(r io.Reader) error { + if !s.authenticated { + return smtp.ErrAuthRequired + } + return s.Session.Data(r) +} + // Implement AuthMechanisms func (s *AuthSession) AuthMechanisms() []string { return []string{"PLAIN"} @@ -116,6 +143,10 @@ func (s *AuthSession) Auth(mech string) (sasl.Server, error) { if err != nil { return fmt.Errorf("invalid credentials: %w", err) } + // Mark session as authenticated and store username + s.authenticated = true + s.username = username + s.Session.AuthenticatedUser = username return nil }), nil case sasl.OAuthBearer: @@ -239,7 +270,8 @@ func (ctx *Context) HandleIncomingEmail(s Session) { } s.Timestamp = time.Now().UTC().Unix() msg := &ReentryCalldata{ - IncomingEmail: &s} + IncomingEmail: &s, + } msgbz, err := json.Marshal(msg) if err != nil { diff --git a/wasmx/app/app.go b/wasmx/app/app.go index 0de79921..078fd077 100644 --- a/wasmx/app/app.go +++ b/wasmx/app/app.go @@ -253,8 +253,6 @@ func init() { // enabled VM extensions for contracts vmp2p.Setup() vmmc.Setup() - - // experimental WIP, do not enable in production: vmcrosschain.Setup() vmsql.Setup() vmpostgresql.Setup() diff --git a/wasmx/cmdutils/testnet.go b/wasmx/cmdutils/testnet.go index a3a9fd72..f7aa42fe 100644 --- a/wasmx/cmdutils/testnet.go +++ b/wasmx/cmdutils/testnet.go @@ -832,6 +832,17 @@ func initTestnetFilesInternal( return err } } + + // If mythos was not generated, copy the first level0 genesis as the default genesis.json + if !generateMythos { + // Copy first level0 genesis to root genesis.json + firstLevel0Genesis := strings.Replace(genFiles[0], ".json", "_"+chainId0+".json", 1) + rootGenesis := genFiles[0] + err := copyFile(firstLevel0Genesis, rootGenesis) + if err != nil { + return err + } + } } if generateOnDemandSingle { diff --git a/wasmx/x/vmkv/ops.go b/wasmx/x/vmkv/ops.go index 9cbe5f6f..4f4544bb 100644 --- a/wasmx/x/vmkv/ops.go +++ b/wasmx/x/vmkv/ops.go @@ -65,7 +65,7 @@ func Connect(_context interface{}, rnh memc.RuntimeHandler, params []interface{} case <-closedChannel: // when close signal is received from Close() API // database is already closed, so we exit this goroutine - ctx.Ctx.Logger().Info(fmt.Sprintf("database connection closed: %s", connId)) + ctx.Ctx.Logger().Info(fmt.Sprintf("kv database connection closed: %s", connId)) return nil } }) diff --git a/wasmx/x/vmsql/ops.go b/wasmx/x/vmsql/ops.go index 252c71a4..7c65358a 100644 --- a/wasmx/x/vmsql/ops.go +++ b/wasmx/x/vmsql/ops.go @@ -5,6 +5,7 @@ import ( "encoding/base64" "encoding/json" "fmt" + "path/filepath" "strings" memc "github.com/loredanacirstea/wasmx/x/wasmx/vm/memory/common" @@ -47,7 +48,10 @@ func Connect(_context interface{}, rnh memc.RuntimeHandler, params []interface{} } } // TODO req.Connection - should we restrict this path and make it relative to our DataDirectory? or introduce a list of allowed directories that WASMX can modify and make sure the path is within these directories. - db, err := sql.Open(req.Driver, req.Connection) + + // connStr := getConnectionStr(req.Connection, ctx.DataDir) + connStr := getConnectionStr(connId, ctx.DataDir) + db, err := sql.Open(req.Driver, connStr) if err != nil { response.Error = err.Error() return prepareResponse(rnh, response) @@ -67,7 +71,7 @@ func Connect(_context interface{}, rnh memc.RuntimeHandler, params []interface{} case <-closedChannel: // when close signal is received from Close() API // database is already closed, so we exit this goroutine - ctx.Ctx.Logger().Info(fmt.Sprintf("database connection closed: %s", connId)) + ctx.Ctx.Logger().Info(fmt.Sprintf("sql database connection closed: %s", connId)) return nil } }) @@ -365,7 +369,6 @@ func prepareResponse(rnh memc.RuntimeHandler, response interface{}) ([]interface if err != nil { return nil, err } - // fmt.Println("--prepareResponse--", string(responsebz)) return rnh.AllocateWriteMem(responsebz) } @@ -427,3 +430,7 @@ func parseSqlQueryParams(params []SqlQueryParam) []interface{} { func buildConnectionId(chainId string, id string, ctx *Context) string { return fmt.Sprintf("%s_%s_%s", chainId, ctx.Env.Contract.Address.String(), id) } + +func getConnectionStr(connId string, dataDir string) string { + return filepath.Join(dataDir, connId+".db") +} diff --git a/wasmx/x/wasmx/client/cli/tx.go b/wasmx/x/wasmx/client/cli/tx.go index 1e51fc69..5dd61840 100644 --- a/wasmx/x/wasmx/client/cli/tx.go +++ b/wasmx/x/wasmx/client/cli/tx.go @@ -67,6 +67,7 @@ func GetTxCmd(wasmVmMeta memc.IWasmVmMeta, ac address.Codec, appCreator multicha InstantiateContractCmd(wasmVmMeta, ac, appCreator), InstantiateContract2Cmd(wasmVmMeta, ac, appCreator), ExecuteContractCmd(wasmVmMeta, ac, appCreator), + ExecuteInternalContractCmd(wasmVmMeta, ac, appCreator), NewProposalExecuteContractCmd(wasmVmMeta, ac, appCreator), ) return txCmd @@ -377,6 +378,48 @@ func ExecuteContractCmd(wasmVmMeta memc.IWasmVmMeta, _ address.Codec, appCreator return cmd } +func ExecuteInternalContractCmd(wasmVmMeta memc.IWasmVmMeta, _ address.Codec, appCreator multichain.NewAppCreator) *cobra.Command { + cmd := &cobra.Command{ + Use: "execute-internal [contract_addr_bech32] [json_encoded_send_args] --amount [coins,optional]", + Short: "Execute a command on an internal wasm contract", + Args: cobra.ExactArgs(2), + RunE: func(cmd *cobra.Command, args []string) error { + clientCtx, err := client.GetClientTxContext(cmd) + if err != nil { + return err + } + mcctx, err := multichain.MultiChainCtxByChainIdWithAppMsgs(wasmVmMeta, clientCtx, cmd.Flags(), []signing.CustomGetSigner{}, appCreator) + if err != nil { + return err + } + txf, err := tx.NewFactoryCLI(mcctx.ClientCtx, cmd.Flags()) + if err != nil { + return err + } + toAddr_, err := mcctx.CustomAddrCodec.StringToAddressPrefixedUnsafe(args[0]) + if err != nil { + return err + } + toAddr := mcctx.CustomAddrCodec.BytesToAccAddressPrefixed(toAddr_.Bytes()) + msg, err := parseExecuteArgs(mcctx.CustomAddrCodec, toAddr, args[1], mcctx.ClientCtx.GetFromAddress(), cmd.Flags()) + if err != nil { + return err + } + if err := msg.ValidateBasic(); err != nil { + return err + } + + return tx.GenerateOrBroadcastTxWithFactory(mcctx.ClientCtx, txf, &msg) + }, + SilenceUsage: true, + } + + cmd.Flags().String(flagAmount, "", "Coins to send to the contract along with command") + multichain.AddMultiChainFlagsToCmd(cmd) + flags.AddTxFlagsToCmd(cmd) + return cmd +} + func parseExecuteArgs(addrCodec address.Codec, contractAddr mcodec.AccAddressPrefixed, execMsg string, sender sdk.AccAddress, flags *flag.FlagSet) (types.MsgExecuteContract, error) { amountStr, err := flags.GetString(flagAmount) if err != nil { diff --git a/wasmx/x/wasmx/keeper/keeper.go b/wasmx/x/wasmx/keeper/keeper.go index 4595f1e4..a6d2fce7 100644 --- a/wasmx/x/wasmx/keeper/keeper.go +++ b/wasmx/x/wasmx/keeper/keeper.go @@ -104,6 +104,7 @@ func NewKeeper( app types.Application, wasmRuntime memc.IWasmVmMeta, ) *Keeper { + dataDir := filepath.Join(homeDir, "data") contractsPath := filepath.Join(homeDir, types.ContractsDir) err := createDirsIfNotExist(contractsPath) if err != nil { @@ -142,7 +143,7 @@ func NewKeeper( panic(err) } - wasmvm, err := NewVM(wasmxAuthority, goRoutineGroup, goContextParent, contractsPath, sourcesDir, wasmConfig.ContractDebugMode, wasmConfig.MemoryCacheSize, app, GetLogger, wasmRuntime) + wasmvm, err := NewVM(wasmxAuthority, goRoutineGroup, goContextParent, dataDir, contractsPath, sourcesDir, wasmConfig.ContractDebugMode, wasmConfig.MemoryCacheSize, app, GetLogger, wasmRuntime) if err != nil { panic(err) } diff --git a/wasmx/x/wasmx/keeper/precompiles.go b/wasmx/x/wasmx/keeper/precompiles.go index 4f5b35d2..04b0497e 100644 --- a/wasmx/x/wasmx/keeper/precompiles.go +++ b/wasmx/x/wasmx/keeper/precompiles.go @@ -152,6 +152,7 @@ func (k *Keeper) ActivateSystemContract( registryAddress mcodec.AccAddressPrefixed, rolesAddress mcodec.AccAddressPrefixed, ) error { + k.Logger(ctx).Info("activating system contract", "label", contract.Label, "code_id", codeID, "role", contract.Role) var err error k.SetSystemContract(ctx, contract) diff --git a/wasmx/x/wasmx/keeper/vm.go b/wasmx/x/wasmx/keeper/vm.go index 6b7b2810..4bb19911 100644 --- a/wasmx/x/wasmx/keeper/vm.go +++ b/wasmx/x/wasmx/keeper/vm.go @@ -26,6 +26,7 @@ type WasmxEngine struct { goRoutineGroup *errgroup.Group goContextParent context.Context DataDir string + WasmxDir string SourcesDir string printDebug bool app types.Application @@ -38,6 +39,7 @@ func NewVM( goRoutineGroup *errgroup.Group, goContextParent context.Context, dataDir string, + wasmxDir string, sourcesDir string, printDebug bool, cacheSize uint32, @@ -50,6 +52,7 @@ func NewVM( goRoutineGroup: goRoutineGroup, goContextParent: goContextParent, DataDir: dataDir, + WasmxDir: wasmxDir, SourcesDir: sourcesDir, printDebug: printDebug, app: app, @@ -61,7 +64,7 @@ func NewVM( func (k *WasmxEngine) Create(wasmBytecode types.WasmCode) (types.Checksum, error) { // get checksum and save wasm checksum := k.checksum(wasmBytecode) - filepath := k.build_path(k.DataDir, checksum) + filepath := k.build_path(k.WasmxDir, checksum) // Read and write permissions for the owner and read-only permissions for everyone else err := utils.SafeWriteFile(filepath, wasmBytecode) @@ -105,10 +108,10 @@ func (k *WasmxEngine) Instantiate( var err error if len(codeInfo.InterpretedBytecodeDeployment) > 0 || types.HasUtf8Dep(codeInfo.Deps) { - data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_INSTANTIATE, env, initMsg, store, cosmosHandler, gasMeter, contractInfo, nil, false, false, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_INSTANTIATE, env, initMsg, store, cosmosHandler, gasMeter, contractInfo, nil, false, false, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } else { // TODO gas - data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_INSTANTIATE, env, initMsg, store, cosmosHandler, gasMeter, contractInfo, nil, false, false, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_INSTANTIATE, env, initMsg, store, cosmosHandler, gasMeter, contractInfo, nil, false, false, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } if err != nil { return types.ContractResponse{}, 0, err @@ -132,9 +135,9 @@ func (k *WasmxEngine) Execute( var err error if len(codeInfo.InterpretedBytecodeRuntime) > 0 || types.HasUtf8Dep(codeInfo.Deps) { - data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_EXECUTE, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, inBackground, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_EXECUTE, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, inBackground, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } else { - data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_EXECUTE, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, inBackground, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_EXECUTE, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, inBackground, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } if err != nil { @@ -161,9 +164,9 @@ func (k *WasmxEngine) ExecuteEntryPoint( // TODO if it has interpreter deps if len(codeInfo.InterpretedBytecodeRuntime) > 0 || types.HasUtf8Dep(codeInfo.Deps) { - data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, contractEntryPoint, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, inBackground, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, contractEntryPoint, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, inBackground, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } else { - data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, contractEntryPoint, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, inBackground, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, contractEntryPoint, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, inBackground, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } if err != nil { @@ -192,9 +195,9 @@ func (k *WasmxEngine) Reply( } if len(codeInfo.InterpretedBytecodeRuntime) > 0 || types.HasUtf8Dep(codeInfo.Deps) { - data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_REPLY, env, wrappedMsgBz, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, false, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_REPLY, env, wrappedMsgBz, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, false, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } else { - data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_REPLY, env, wrappedMsgBz, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, false, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_REPLY, env, wrappedMsgBz, store, cosmosHandler, gasMeter, contractInfo, dependencies, false, false, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } if err != nil { @@ -218,9 +221,9 @@ func (k *WasmxEngine) QueryExecute( var data types.ContractResponse var err error if len(codeInfo.InterpretedBytecodeRuntime) > 0 || types.HasUtf8Dep(codeInfo.Deps) { - data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_QUERY, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, isdebug, false, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasmInterpreted(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_QUERY, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, isdebug, false, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } else { - data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_QUERY, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, isdebug, false, k.app, k.WasmRuntime.NewWasmVm) + data, err = vm.ExecuteWasm(k.wasmxAuthority, k.goRoutineGroup, k.goContextParent, ctx, k.Logger, types.ENTRY_POINT_QUERY, env, executeMsg, store, cosmosHandler, gasMeter, contractInfo, dependencies, isdebug, false, k.app, k.WasmRuntime.NewWasmVm, k.DataDir) } if err != nil { return data, 0, err @@ -245,7 +248,7 @@ func (k *WasmxEngine) Cleanup() { } func (k *WasmxEngine) Pin(ctx sdk.Context, checksum types.Checksum, compiledFolderPath string, meteringOff bool) error { - pinnedPath := k.build_path_pinned(k.DataDir, checksum) + pinnedPath := k.build_path_pinned(k.WasmxDir, checksum) // if we do not find a precompiled contract in our cache, we compile it here if compiledFolderPath != "" { compiledPath := k.build_path(compiledFolderPath, checksum) @@ -254,7 +257,7 @@ func (k *WasmxEngine) Pin(ctx sdk.Context, checksum types.Checksum, compiledFold return nil } } - err := k.pin_code(ctx, k.build_path(k.DataDir, checksum), pinnedPath, meteringOff) + err := k.pin_code(ctx, k.build_path(k.WasmxDir, checksum), pinnedPath, meteringOff) if err != nil { return err } @@ -288,7 +291,7 @@ func (k *WasmxEngine) load_utf8(extension string, checksum types.Checksum) ([]by } func (k *WasmxEngine) load_wasm(checksum types.Checksum) (types.WasmCode, error) { - filepath := k.build_path(k.DataDir, checksum) + filepath := k.build_path(k.WasmxDir, checksum) return os.ReadFile(filepath) } @@ -306,11 +309,11 @@ func (k *WasmxEngine) build_path_utf8(dataDir string, checksum types.Checksum, e } func (k *WasmxEngine) BuildPathPinned(codeHash []byte) string { - return k.build_path_pinned(k.DataDir, codeHash) + return k.build_path_pinned(k.WasmxDir, codeHash) } func (k *WasmxEngine) BuildPath(codeHash []byte) string { - return k.build_path(k.DataDir, codeHash) + return k.build_path(k.WasmxDir, codeHash) } func (k *WasmxEngine) BuildPathUtf8(codeHash []byte, extension string) string { diff --git a/wasmx/x/wasmx/types/system_contract.go b/wasmx/x/wasmx/types/system_contract.go index d50cae65..e942e31f 100644 --- a/wasmx/x/wasmx/types/system_contract.go +++ b/wasmx/x/wasmx/types/system_contract.go @@ -918,7 +918,7 @@ func DefaultSystemContracts(accBech32Codec mcodec.AccBech32Codec, feeCollectorBe precompiles = append(precompiles, consensusPrecompiles...) precompiles = append(precompiles, MultiChainPrecompiles(minValidatorCount, enableEIDCheck, erc20CodeId, derc20CodeId)...) precompiles = append(precompiles, ChatPrecompiles()...) - // precompiles = append(precompiles, SpecialPrecompiles()...) + precompiles = append(precompiles, SpecialPrecompiles()...) precompiles, err := FillRoles(precompiles, accBech32Codec, feeCollectorBech32) if err != nil { @@ -1019,7 +1019,7 @@ func DefaultTimeChainContracts(accBech32Codec mcodec.AccBech32Codec, feeCollecto precompiles = append(precompiles, consensusPrecompiles...) precompiles = append(precompiles, MultiChainPrecompiles(minValidatorCount, enableEIDCheck, erc20CodeId, derc20CodeId)...) precompiles = append(precompiles, ChatPrecompiles()...) - // precompiles = append(precompiles, SpecialPrecompiles()...) + precompiles = append(precompiles, SpecialPrecompiles()...) precompiles, err = FillRoles(precompiles, accBech32Codec, feeCollectorBech32) if err != nil { @@ -1063,7 +1063,7 @@ func DefaultOnDemandSingleContracts(accBech32Codec mcodec.AccBech32Codec, feeCol precompiles = append(precompiles, consensusPrecompiles...) precompiles = append(precompiles, MultiChainPrecompiles(minValidatorCount, enableEIDCheck, erc20CodeId, derc20CodeId)...) precompiles = append(precompiles, ChatPrecompiles()...) - // precompiles = append(precompiles, SpecialPrecompiles()...) + precompiles = append(precompiles, SpecialPrecompiles()...) precompiles, err := FillRoles(precompiles, accBech32Codec, feeCollectorBech32) if err != nil { diff --git a/wasmx/x/wasmx/vm/ops_common.go b/wasmx/x/wasmx/vm/ops_common.go index f8676568..2dcb7fcc 100644 --- a/wasmx/x/wasmx/vm/ops_common.go +++ b/wasmx/x/wasmx/vm/ops_common.go @@ -238,6 +238,7 @@ func WasmxCall(ctx *Context, req vmtypes.CallRequestCommon) (int32, []byte) { dbIterators: map[int32]types.Iterator{}, RuntimeHandler: rnh, newIVmFn: ctx.newIVmFn, + DataDir: ctx.DataDir, ContractInfo: destContractInfo, CurrentSubCallLevel: ctx.CurrentSubCallLevel + 1, CurrentSubCallId: ctx.CurrentSubCallLevelCount, diff --git a/wasmx/x/wasmx/vm/precompiles/63.wasmx_email_0.0.1.wasm b/wasmx/x/wasmx/vm/precompiles/63.wasmx_email_0.0.1.wasm index a8f4f79b..33bb9426 100644 Binary files a/wasmx/x/wasmx/vm/precompiles/63.wasmx_email_0.0.1.wasm and b/wasmx/x/wasmx/vm/precompiles/63.wasmx_email_0.0.1.wasm differ diff --git a/wasmx/x/wasmx/vm/types.go b/wasmx/x/wasmx/vm/types.go index 6ba3f554..3c555047 100644 --- a/wasmx/x/wasmx/vm/types.go +++ b/wasmx/x/wasmx/vm/types.go @@ -53,6 +53,7 @@ type Context struct { RuntimeHandler memc.RuntimeHandler ContractInfo *types.ContractDependency newIVmFn memc.NewIVmFn + DataDir string } // not used at this point @@ -82,6 +83,7 @@ func (c *Context) Clone() *Context { RuntimeHandler: c.RuntimeHandler, ContractInfo: c.ContractInfo.Clone(), newIVmFn: c.newIVmFn, + DataDir: c.DataDir, } } diff --git a/wasmx/x/wasmx/vm/vm.go b/wasmx/x/wasmx/vm/vm.go index cc3d1da4..9021823b 100644 --- a/wasmx/x/wasmx/vm/vm.go +++ b/wasmx/x/wasmx/vm/vm.go @@ -257,6 +257,7 @@ func ExecuteWasmInterpreted( inBackground bool, app types.Application, newIVmFn memc.NewIVmFn, + dataDir string, ) (types.ContractResponse, error) { var err error var ethMsg types.WasmxExecutionMessage @@ -287,6 +288,7 @@ func ExecuteWasmInterpreted( RuntimeHandler: rnh, ContractInfo: &contractInfo, newIVmFn: newIVmFn, + DataDir: dataDir, } context.Env.CurrentCall.CallData = ethMsg.Data for i := range dependencies { @@ -371,6 +373,7 @@ func ExecuteWasm( inBackground bool, app types.Application, newIVmFn memc.NewIVmFn, + dataDir string, ) (types.ContractResponse, error) { var err error var ethMsg types.WasmxExecutionMessage @@ -401,6 +404,7 @@ func ExecuteWasm( RuntimeHandler: rnh, ContractInfo: &contractInfo, newIVmFn: newIVmFn, + DataDir: dataDir, } context.Env.CurrentCall.CallData = ethMsg.Data