github.com/machinefi/w3bstream@v1.6.5-rc9.0.20240426031326-b8c7c4876e72/pkg/modules/account/account_eth.go (about) 1 package account 2 3 import ( 4 "context" 5 "github.com/machinefi/w3bstream/pkg/depends/kit/logr" 6 "strings" 7 8 // "github.com/spruceid/siwe-go" 9 10 confid "github.com/machinefi/w3bstream/pkg/depends/conf/id" 11 "github.com/machinefi/w3bstream/pkg/depends/kit/sqlx" 12 "github.com/machinefi/w3bstream/pkg/enums" 13 "github.com/machinefi/w3bstream/pkg/errors/status" 14 "github.com/machinefi/w3bstream/pkg/models" 15 "github.com/machinefi/w3bstream/pkg/modules/operator" 16 "github.com/machinefi/w3bstream/pkg/types" 17 "github.com/spruceid/siwe-go" 18 ) 19 20 func FetchOrCreateAccountByEthAddress(ctx context.Context, address types.EthAddress) (*models.Account, *models.AccountIdentity, error) { 21 _, l := logr.Start(ctx, "account.FetchOrCreateAccountByEthAddress") 22 defer l.End() 23 24 d := types.MustMgrDBExecutorFromContext(ctx) 25 g := confid.MustSFIDGeneratorFromContext(ctx) 26 27 var ( 28 rel = models.RelAccount{AccountID: g.MustGenSFID()} 29 acc *models.Account 30 aci *models.AccountIdentity 31 exists bool 32 ) 33 34 err := sqlx.NewTasks(d).With( 35 // fetch AccountIdentity 36 func(db sqlx.DBExecutor) error { 37 aci = &models.AccountIdentity{ 38 AccountIdentityInfo: models.AccountIdentityInfo{ 39 Type: enums.ACCOUNT_IDENTITY_TYPE__ETHADDRESS, 40 IdentityID: address.String(), 41 }, 42 } 43 err := aci.FetchByTypeAndIdentityID(db) 44 if err != nil { 45 if sqlx.DBErr(err).IsNotFound() { 46 exists = false 47 return nil 48 } else { 49 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 50 } 51 } else { 52 exists = true 53 rel.AccountID = aci.AccountID 54 return nil 55 } 56 }, 57 // create or fetch Account 58 func(db sqlx.DBExecutor) error { 59 acc = &models.Account{RelAccount: rel} 60 if exists { 61 if err := acc.FetchByAccountID(db); err != nil { 62 if sqlx.DBErr(err).IsNotFound() { 63 return status.AccountNotFound 64 } 65 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 66 } 67 if acc.State != enums.ACCOUNT_STATE__ENABLED { 68 return status.DisabledAccount 69 } 70 return nil 71 } else { 72 acc.Role = enums.ACCOUNT_ROLE__DEVELOPER 73 acc.State = enums.ACCOUNT_STATE__ENABLED 74 if err := acc.Create(db); err != nil { 75 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 76 } 77 return nil 78 } 79 }, 80 // create AccountIdentity 81 func(db sqlx.DBExecutor) error { 82 if exists { 83 return nil 84 } 85 aci.RelAccount = rel 86 aci.Source = enums.ACCOUNT_SOURCE__SUBMIT 87 if err := aci.Create(db); err != nil { 88 if sqlx.DBErr(err).IsConflict() { 89 return status.AccountIdentityConflict 90 } 91 return status.DatabaseError.StatusErr().WithDesc(err.Error()) 92 } 93 return nil 94 }, 95 func(d sqlx.DBExecutor) error { 96 if exists { 97 return nil 98 } 99 req := operator.CreateReq{ 100 Name: operator.DefaultOperatorName, 101 PrivateKey: generateRandomPrivateKey(), 102 } 103 ctx := types.WithAccount(types.WithMgrDBExecutor(ctx, d), acc) 104 _, err := operator.Create(ctx, &req) 105 return err 106 }, 107 ).Do() 108 109 if err != nil { 110 l.Error(err) 111 return nil, nil, err 112 } 113 return acc, aci, nil 114 } 115 116 type LoginByEthAddressReq struct { 117 Message string `json:"message"` // Message siwe serialized message 118 Signature string `json:"signature"` // Signature should have '0x' prefix 119 } 120 121 func ValidateLoginByEthAddress(ctx context.Context, r *LoginByEthAddressReq) (*models.Account, error) { 122 _, l := logr.Start(ctx, "account.ValidateLoginByEthAddress") 123 defer l.End() 124 125 msg, err := siwe.ParseMessage(r.Message) 126 if err != nil { 127 l.Error(err) 128 return nil, status.InvalidEthLoginMessage.StatusErr().WithDesc(err.Error()) 129 } 130 131 if _, err = msg.Verify(r.Signature, nil, nil, nil); err != nil { 132 l.Error(err) 133 return nil, status.InvalidEthLoginSignature.StatusErr().WithDesc(err.Error()) 134 } 135 136 address := strings.ToLower(msg.GetAddress().String()) 137 138 if lst, ok := types.EthAddressWhiteListFromContext(ctx); ok { 139 if !lst.Validate(address) { 140 return nil, status.WhiteListForbidden 141 } 142 } 143 144 acc, _, err := FetchOrCreateAccountByEthAddress(ctx, types.EthAddress(address)) 145 146 if err != nil { 147 l.Error(err) 148 return nil, err 149 } 150 return acc, nil 151 }