This commit is contained in:
hoernschen 2020-10-11 23:11:30 +02:00
parent 7db9c374cc
commit 9eac960763
26 changed files with 3119 additions and 266 deletions

View file

@ -1,8 +0,0 @@
package event
// TODO: Check if it can be deleted
type EDU struct {
Type string `json:"type,omitempty"`
Content string `json:"content,omitempty"`
}

View file

@ -1,10 +1,123 @@
package event
type Event struct {
Id string `json:"id,omitempty"`
RoomId string `json:"roomId,omitempty"`
EventType string `json:"eventType,omitempty"`
Content string `json:"content,omitempty"`
ParentId string `json:"parent,omitempty"`
Depth int `json:"depth,omitempty"`
Id string `json:"event_id,omitempty"`
RoomId string `json:"room_id,omitempty"`
Sender string `json:"sender,omitempty"`
Origin string `json:"origin,omitempty"`
Timestamp int64 `json:"origin_server_ts,omitempty"`
EventType string `json:"type,omitempty"`
StateKey string `json:"state_key,omitempty"`
Content string `json:"content,omitempty"`
PrevEventHashes map[string]EventHash `json:"prev_events,omitempty"`
Depth int `json:"depth,omitempty"`
AuthEventHashes map[string]EventHash `json:"auth_events,omitempty"`
Unsigned UnsignedData `json:"unsigned,omitempty"`
Hashes EventHash `json:"hashes,omitempty"`
Signatures map[string]map[string]string `json:"signatures,omitempty"`
}
type StateEvent struct {
EventType string `json:"type,omitempty"`
StateKey string `json:"state_key,omitempty"`
Content string `json:"content,omitempty"`
}
type UnsignedData struct {
Age int `json:"age,omitempty"`
TransactionId string `json:"transaction_id,omitempty"`
ReplaceState string `json:"replaces_state,omitempty"`
PrevSender string `json:"prev_sender,omitempty"`
PrevContent string `json:"prev_content,omitempty"`
RedactedBecause string `json:"redacted_because,omitempty"`
}
type EventHash struct {
SHA256 string `json:"sha256,omitempty"`
}
type CreateEventContent struct {
Creator string `json:"creator,omitempty"`
Federated bool `json:"m.federate,omitempty"`
RoomVersion string `json:"room_version,omitempty"`
}
type JoinRuleEventContent struct {
JoinRule string `json:"join_rule,omitempty"`
}
type HistoryVisibilityEventContent struct {
HistoryVisibility string `json:"history_visibility,omitempty"`
}
type GuestAccessEventContent struct {
GuestAccess string `json:"guest_access,omitempty"`
}
type NameEventContent struct {
Name string `json:"name,omitempty"`
}
type TopicEventContent struct {
Topic string `json:"topic,omitempty"`
}
type MemberEventContent struct {
AvatarUrl string `json:"avatar_url,omitempty"`
DisplayName string `json:"displayname,omitempty"`
Membership string `json:"membership,omitempty"`
IsDirect bool `json:"is_direct,omitempty"`
}
type PowerLevelsEventContent struct {
Ban int `json:"ban,omitempty"`
Events map[string]int `json:"events,omitempty"`
EventsDefault int `json:"events_default,omitempty"`
Invite int `json:"invite,omitempty"`
Kick int `json:"kick,omitempty"`
Redact int `json:"redact,omitempty"`
StateDefault int `json:"state_default,omitempty"`
Users map[string]int `json:"users,omitempty"`
UsersDefault int `json:"users_default,omitempty"`
Notifications Notifications `json:"notifications,omitempty"`
}
type Notifications struct {
Room int `json:"room,omitempty"`
}
type sendMessageRequest struct {
MessageType string `json:"msgtype,omitempty"`
Body string `json:"body,omitempty"`
}
type createEventResponse struct {
EventId string `json:"event_id,omitempty"`
}
type getEventRequest struct{}
type getEventResponse struct {
Content string `json:"content,omitempty"`
EventType string `json:"type,omitempty"`
}
type syncEventsServerRequest struct {
Origin string `json:"origin,omitempty"`
Timestamp int64 `json:"origin_server_ts,omitempty"`
PDUs []*Event `json:"pdus,omitempty"`
}
type syncEventsServerResponse struct {
PDUs map[string]pduProcessingResult `json:"pdus,omitempty"`
}
type backfillResponse struct {
Origin string `json:"origin,omitempty"`
Timestamp int64 `json:"origin_server_ts,omitempty"`
PDUs []*Event `json:"pdus,omitempty"`
}
type pduProcessingResult struct {
ProcessingError string `json:"error,omitempty"`
}

View file

@ -1,5 +1,803 @@
package event
func New() (event *Event) {
import (
"bytes"
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"time"
"github.com/cenkalti/backoff/v4"
"github.com/gorilla/mux"
"nutfactory.org/Matrix/config"
"nutfactory.org/Matrix/entities/device"
"nutfactory.org/Matrix/entities/user"
"nutfactory.org/Matrix/utils"
)
func New(
roomId string,
sender string,
origin string,
timestamp int64,
eventType string,
stateKey string,
content string,
txnId string,
) (err error, newEvent *Event) {
err, eventId := utils.CreateUUID()
if err != nil {
return
}
id := generateEventId(eventId)
newEvent = &Event{
Id: id,
RoomId: roomId,
Sender: sender,
Origin: origin,
Timestamp: timestamp,
EventType: eventType,
StateKey: stateKey,
Content: content,
Unsigned: UnsignedData{
TransactionId: txnId,
},
}
newEvent.AuthEventHashes, err = GetAuthEvents(newEvent)
if err != nil {
return
}
if eventType != "m.room.create" {
var depth int
newEvent.PrevEventHashes, depth, err = ReadEventsWithoutChild(roomId)
if err != nil {
return
}
newEvent.Depth = depth + 1
}
newEvent.AuthEventHashes, err = GetAuthEvents(newEvent)
if err != nil {
return
}
newEventBytesForHash, err := json.Marshal(newEvent)
if err != nil {
return
}
err, newEvent.Hashes.SHA256 = utils.Hash(newEventBytesForHash)
if err != nil {
return
}
newEvent.Unsigned = UnsignedData{}
newEventBytesForSign, err := json.Marshal(newEvent)
if err != nil {
return
}
newEvent.Signatures = utils.SignContent(newEventBytesForSign)
newEvent.Unsigned = UnsignedData{
TransactionId: txnId,
}
return
}
func SendMessageHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := sendMessageRequest{}
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
token, errResponse := utils.GetAccessToken(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
foundUser, err := user.ReadUserFromAccessToken(token)
if err != nil || foundUser == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_UNKNOWN_TOKEN", ErrorMessage: fmt.Sprintf("%s", err)}); err != nil {
panic(err)
}
return
}
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
roomId := vars["roomId"]
eventType := vars["eventType"]
txnId := vars["txnId"]
if roomId == "" || eventType == "" || txnId == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
content := buf.String()
err, newEvent := New(
roomId,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
eventType,
foundUser.Id,
content,
txnId,
)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Creating Event: %s", err)}); err != nil {
panic(err)
}
return
}
err = CreateEvent(newEvent, txnId)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
transaction := &Transaction{
Id: txnId,
Origin: config.Homeserver,
Timestamp: time.Now().Unix(),
PDUS: []*Event{newEvent},
}
servers, err := ReadServers(roomId)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
for _, server := range servers {
operation := func() error {
return SendTransaction(transaction, server)
}
notify := func(err error, duration time.Duration) {
log.Printf("Error Sending Transaction, retrying in %ss: %s", duration/1000000000, err)
}
go backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
}
response := createEventResponse{
EventId: newEvent.Id,
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func CreateStateEventHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
token, errResponse := utils.GetAccessToken(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
foundUser, err := user.ReadUserFromAccessToken(token)
if err != nil || foundUser == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_UNKNOWN_TOKEN", ErrorMessage: fmt.Sprintf("%s", err)}); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
roomId := vars["roomId"]
eventType := vars["eventType"]
stateKey := vars["stateKey"]
if roomId == "" || eventType == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
buf := new(bytes.Buffer)
buf.ReadFrom(r.Body)
content := buf.String()
err, newEvent := New(
roomId,
foundUser.Id,
config.Homeserver,
time.Now().Unix(),
eventType,
stateKey,
content,
"",
)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Error Creating Event: %s", err)}); err != nil {
panic(err)
}
return
}
err = CreateEvent(newEvent, "")
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
err, txnId := utils.CreateUUID()
transaction := &Transaction{
Id: txnId,
Origin: config.Homeserver,
Timestamp: time.Now().Unix(),
PDUS: []*Event{newEvent},
}
servers, err := ReadServers(roomId)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Database Error: %s", err)}); err != nil {
panic(err)
}
return
}
for _, server := range servers {
operation := func() error {
return SendTransaction(transaction, server)
}
notify := func(err error, duration time.Duration) {
log.Printf("Error Sending Transaction, retrying in %ss: %s", duration/1000000000, err)
}
go backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
}
response := createEventResponse{
EventId: newEvent.Id,
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func GetEventUserHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := getEventRequest{}
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
token, errResponse := utils.GetAccessToken(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
foundUser, err := user.ReadUserFromAccessToken(token)
if err != nil || foundUser == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_UNKNOWN_TOKEN", ErrorMessage: fmt.Sprintf("%s", err)}); err != nil {
panic(err)
}
return
}
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
roomId := vars["roomId"]
eventId := vars["eventId"]
if roomId == "" || eventId == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
foundEvent, err := ReadEvent(eventId)
if err != nil || foundEvent == nil {
w.WriteHeader(http.StatusNotFound)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_NOT_FOUND", ErrorMessage: fmt.Sprintf("Event not found. %s", err)}); err != nil {
panic(err)
}
return
}
response := getEventResponse{
Content: foundEvent.Content,
EventType: foundEvent.EventType,
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func GetStateEventHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
token, errResponse := utils.GetAccessToken(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
foundUser, err := user.ReadUserFromAccessToken(token)
if err != nil || foundUser == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_UNKNOWN_TOKEN", ErrorMessage: fmt.Sprintf("%s", err)}); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
roomId := vars["roomId"]
eventType := vars["eventType"]
stateKey := vars["stateKey"]
if roomId == "" || eventType == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
foundEvent, err := ReadStateEvent(roomId, eventType, stateKey)
if err != nil || foundEvent == nil {
w.WriteHeader(http.StatusNotFound)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorCode: "M_NOT_FOUND", ErrorMessage: fmt.Sprintf("Event not found. %s", err)}); err != nil {
panic(err)
}
return
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(foundEvent.Content); err != nil {
panic(err)
}
}
func SyncEventsServerHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
request := syncEventsServerRequest{}
errResponse := utils.CheckRequest(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
errResponse = utils.CheckAuthHeader(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&request)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: fmt.Sprintf("Could not parse JSON: %s", err)}); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
txnId := vars["txnId"]
if txnId == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
response := syncEventsServerResponse{
PDUs: make(map[string]pduProcessingResult),
}
missingEventIds := make(map[string][]string)
for _, pdu := range request.PDUs {
signatureValid := CheckSignature(*pdu)
if !signatureValid {
log.Printf("Wrong Signature for Event %s", pdu.Id)
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: "Signature not valid"}
continue
}
authEventsValid, err := CheckAuthEvents(pdu)
if !authEventsValid || err != nil {
log.Printf("Wrong Auth Events for Event %s", pdu.Id)
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: fmt.Sprintf("Error in Auth Check: %s", err)}
//continue
}
missingParentIds, err := CheckParents(pdu)
if len(missingParentIds) > 0 || err != nil {
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: fmt.Sprintf("Error in Parents Check: %s", err)}
for _, parentId := range missingParentIds {
missingEventIds[pdu.RoomId] = append(missingEventIds[pdu.RoomId], parentId)
}
}
foundEvent, err := ReadEvent(pdu.Id)
if foundEvent == nil && err == nil {
err = CreateEvent(pdu, txnId)
}
if err != nil {
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: fmt.Sprintf("Database Error: %s", err)}
continue
}
err = HandleEvent(pdu)
if err != nil {
response.PDUs[pdu.Id] = pduProcessingResult{ProcessingError: fmt.Sprintf("Error in Event-Handling: %s", err)}
continue
}
response.PDUs[pdu.Id] = pduProcessingResult{}
}
if len(missingEventIds) > 0 {
for roomId, eventIds := range missingEventIds {
operation := func() error {
return Backfill(eventIds, roomId, request.Origin)
}
notify := func(err error, duration time.Duration) {
log.Printf("Error Backfill, retrying in %s: %s", duration/1000000000, err)
}
go backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify)
}
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func BackfillHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
errResponse := utils.CheckAuthHeader(r)
if errResponse != nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(errResponse); err != nil {
panic(err)
}
return
}
vars := mux.Vars(r)
roomId := vars["roomId"]
if roomId == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
limit := 50
eventIds, ok := r.URL.Query()["v"]
log.Printf("%s", eventIds)
if !ok || eventIds[0] == "" {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Missing Parameter"}); err != nil {
panic(err)
}
return
}
limitString := r.URL.Query().Get("limit")
if limitString != "" {
limit, _ = strconv.Atoi(limitString)
}
pdus := []*Event{}
for len(pdus) < limit {
newEventIds := []string{}
for _, eventId := range eventIds {
foundEvent, err := ReadEvent(eventId)
if err != nil || foundEvent == nil {
w.WriteHeader(http.StatusBadRequest)
if err := json.NewEncoder(w).Encode(utils.ErrorResponse{ErrorMessage: "Event not found"}); err != nil {
panic(err)
}
return
}
for newEventId, _ := range foundEvent.PrevEventHashes {
newEventIds = append(newEventIds, newEventId)
}
pdus = append(pdus, foundEvent)
}
eventIds = newEventIds
}
response := backfillResponse{
Origin: config.Homeserver,
Timestamp: time.Now().Unix(),
PDUs: pdus,
}
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
panic(err)
}
}
func SendTransaction(transaction *Transaction, homeserver string) (err error) {
requestUrl := fmt.Sprintf("https://%s/_matrix/federation/v1/send/%s?", homeserver, transaction.Id)
request := syncEventsServerRequest{
Origin: transaction.Origin,
Timestamp: transaction.Timestamp,
PDUs: transaction.PDUS,
}
reqBody, err := json.Marshal(request)
if err != nil {
return
}
client := &http.Client{}
req, err := http.NewRequest(http.MethodPut, requestUrl, bytes.NewBuffer(reqBody))
if err != nil {
return
}
_, err = client.Do(req)
return
}
func Backfill(eventIds []string, roomId string, homeserver string) (err error) {
requestUrl := fmt.Sprintf("https://%s/_matrix/federation/v1/backfill/%s?", homeserver, roomId)
for _, eventId := range eventIds {
requestUrl = fmt.Sprintf("%sv=%s&", requestUrl, eventId)
}
r, err := http.Get(requestUrl)
if err != nil {
return
}
response := backfillResponse{}
defer r.Body.Close()
decoder := json.NewDecoder(r.Body)
err = decoder.Decode(&response)
if err != nil {
return
}
var missingEventIds []string
for _, pdu := range response.PDUs {
for i, eventId := range missingEventIds {
if pdu.Id == eventId {
missingEventIds = append(missingEventIds[:i], missingEventIds[i+1:]...)
}
}
signatureValid := CheckSignature(*pdu)
if !signatureValid {
log.Printf("Wrong Signature for Event %s", pdu.Id)
missingEventIds = append(missingEventIds, pdu.Id)
continue
}
authEventsValid, err := CheckAuthEvents(pdu)
if !authEventsValid || err != nil {
log.Printf("Wrong Auth Events for Event %s", pdu.Id)
missingEventIds = append(missingEventIds, pdu.Id)
continue
}
missingParentIds, err := CheckParents(pdu)
if len(missingParentIds) > 0 || err != nil {
for _, parentId := range missingParentIds {
missingEventIds = append(missingEventIds, parentId)
}
}
foundEvent, err := ReadEvent(pdu.Id)
if foundEvent == nil && err == nil {
err = CreateEvent(pdu, "")
}
if err != nil {
continue
}
err = HandleEvent(pdu)
if err != nil {
log.Printf("Error in Event-Handling: %s", err)
continue
}
}
if len(missingEventIds) > 0 {
Backfill(missingEventIds, roomId, homeserver)
}
return
}
func generateEventId(id string) string {
return fmt.Sprintf("$%s:%s", id, config.Homeserver)
}
func GetAuthChain(newEvent *Event) (authChain []*Event, err error) {
createEvent, err := ReadStateEvent(newEvent.RoomId, "m.room.create", "")
if err != nil {
return
}
if createEvent != nil {
authChain = append(authChain, createEvent)
}
powerLevelEvent, err := ReadStateEvent(newEvent.RoomId, "m.room.power_levels", "")
if err != nil {
return
}
if powerLevelEvent != nil {
authChain = append(authChain, powerLevelEvent)
}
stateKey := newEvent.Sender
if newEvent.EventType == "m.room.member" {
stateKey = newEvent.StateKey
}
memberEvent, err := ReadStateEvent(newEvent.RoomId, "m.room.member", stateKey)
if err != nil {
return
}
if memberEvent != nil {
authChain = append(authChain, memberEvent)
}
joinRuleEvent, err := ReadStateEvent(newEvent.RoomId, "m.room.join_rules", "")
if err != nil {
return
}
if joinRuleEvent != nil && newEvent.EventType == "m.room.member" {
authChain = append(authChain, joinRuleEvent)
}
return
}
func GetAuthEvents(newEvent *Event) (authEventHashes map[string]EventHash, err error) {
authEventHashes = make(map[string]EventHash)
authChain, err := GetAuthChain(newEvent)
if err != nil {
return
}
for _, authEvent := range authChain {
authEventHashes[authEvent.Id] = authEvent.Hashes
}
return
}
func CheckEventHash(id string, hash EventHash) (correct bool, eventFound bool, err error) {
foundEvent, err := ReadEvent(id)
correct = true
eventFound = true
if err != nil {
return
}
if foundEvent == nil {
eventFound = false
return
}
if hash.SHA256 != foundEvent.Hashes.SHA256 {
correct = false
return
}
return
}
func CheckParents(eventToCheck *Event) (missingParentIds []string, err error) {
for key, hash := range eventToCheck.PrevEventHashes {
correctHash, foundEvent, err := CheckEventHash(key, hash)
if !correctHash || !foundEvent || err != nil {
missingParentIds = append(missingParentIds, key)
}
}
return
}
func CheckAuthEvents(eventToCheck *Event) (correct bool, err error) {
correct = true
authEvents, err := GetAuthEvents(eventToCheck)
if err != nil {
return
}
for key, hash := range authEvents {
if eventToCheck.AuthEventHashes[key].SHA256 != hash.SHA256 {
correct = false
return
}
}
return
}
func CheckSignature(eventToCheck Event) (correct bool) {
correct = false
signatures := eventToCheck.Signatures
eventToCheck.Unsigned = UnsignedData{}
eventToCheck.Signatures = nil
jsonString, err := json.Marshal(eventToCheck)
if err != nil {
return
}
for id, signature := range signatures[eventToCheck.Sender] {
key, err := device.ReadKey(id)
if err == nil {
correct = utils.VerifySignature([]byte(key.Key), jsonString, []byte(signature))
}
}
return
}
func HandleEvents(events []*Event) (err error) {
for _, eventToHandle := range events {
err = HandleEvent(eventToHandle)
}
return
}
func HandleEvent(eventToHandle *Event) (err error) {
if eventToHandle.EventType == "m.room.message" {
message := sendMessageRequest{}
err = json.Unmarshal([]byte(eventToHandle.Content), &message)
if err != nil {
return
}
if message.MessageType != "" && message.MessageType == "m.text" {
log.Printf("%s: %s", eventToHandle.Sender, message.Body)
}
} else if eventToHandle.EventType == "m.room.member" {
message := MemberEventContent{}
err = json.Unmarshal([]byte(eventToHandle.Content), &message)
if err != nil {
return
}
if message.Membership == "join" {
CreateRoomMember(eventToHandle.RoomId, eventToHandle.StateKey)
}
}
return
}

View file

@ -2,15 +2,16 @@ package event
import (
"fmt"
"strings"
"nutfactory.org/Matrix/utils/database"
)
func CreateEvent(event *Event, txnId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO event
(id, roomId, txnId, eventType, content, parentId, depth)
func CreateRoomMember(roomId string, userId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO roomMember
(roomId, userId, server)
VALUES
(?, ?, ?, ?, ?, ?, ?)`)
(?, ?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
@ -23,7 +24,7 @@ func CreateEvent(event *Event, txnId string) (err error) {
}
defer stmt.Close()
_, err = stmt.Exec(event.Id, event.RoomId, txnId, event.EventType, event.Content, event.ParentId, event.Depth)
_, err = stmt.Exec(roomId, userId, strings.Split(userId, ":")[1])
if err != nil {
return
}
@ -31,11 +32,129 @@ func CreateEvent(event *Event, txnId string) (err error) {
return
}
func CreateParents(eventId string, parentIds map[string]EventHash) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO parent
(eventId, parentId)
VALUES
(?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
return
}
stmt, err := tx.Prepare(sqlStmt)
if err != nil {
return
}
defer stmt.Close()
for parentId, _ := range parentIds {
_, err = stmt.Exec(
eventId,
parentId,
)
if err != nil {
return
}
}
tx.Commit()
return
}
func CreateAuthEvents(eventId string, authEventIds map[string]EventHash) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO authEvent
(eventId, authEventId)
VALUES
(?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
return
}
stmt, err := tx.Prepare(sqlStmt)
if err != nil {
return
}
defer stmt.Close()
for authEventId, _ := range authEventIds {
_, err = stmt.Exec(
eventId,
authEventId,
)
if err != nil {
return
}
}
tx.Commit()
return
}
func CreateEvent(event *Event, txnId string) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO event
(id, roomId, txnId, sender, origin, timestamp, eventType, stateKey, content, depth, hash, signature)
VALUES
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
return
}
stmt, err := tx.Prepare(sqlStmt)
if err != nil {
return
}
defer stmt.Close()
signatures := ""
for _, signature := range event.Signatures[event.Origin] {
signatures = signature
}
_, err = stmt.Exec(
event.Id,
event.RoomId,
txnId,
event.Sender,
event.Origin,
event.Timestamp,
event.EventType,
event.StateKey,
event.Content,
event.Depth,
event.Hashes.SHA256,
signatures,
)
if err != nil {
return
}
tx.Commit()
err = CreateParents(event.Id, event.PrevEventHashes)
if err != nil {
return
}
err = CreateAuthEvents(event.Id, event.AuthEventHashes)
if err != nil {
return
}
return
}
func CreateEventsFromTransaction(txnId string, pdus map[string]*Event) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO event
(id, roomId, txnId, eventType, content, parentId, depth)
(id, roomId, txnId, sender, origin, timestamp, eventType, stateKey, content, depth, hash, signature)
VALUES
(?, ?, ?, ?, ?, ?, ?)`)
(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
@ -49,7 +168,35 @@ func CreateEventsFromTransaction(txnId string, pdus map[string]*Event) (err erro
defer stmt.Close()
for _, pdu := range pdus {
_, err = stmt.Exec(pdu.Id, pdu.RoomId, txnId, pdu.EventType, pdu.Content, pdu.ParentId, pdu.Depth)
signatures := ""
for _, signature := range pdu.Signatures[pdu.Origin] {
signatures = signature
}
_, err = stmt.Exec(
pdu.Id,
pdu.RoomId,
txnId,
pdu.Sender,
pdu.Origin,
pdu.Timestamp,
pdu.EventType,
pdu.StateKey,
pdu.Content,
pdu.Depth,
pdu.Hashes.SHA256,
signatures,
)
if err != nil {
return
}
err = CreateParents(pdu.Id, pdu.PrevEventHashes)
if err != nil {
return
}
err = CreateAuthEvents(pdu.Id, pdu.AuthEventHashes)
if err != nil {
return
}
@ -59,8 +206,186 @@ func CreateEventsFromTransaction(txnId string, pdus map[string]*Event) (err erro
return
}
func ReadEventHash(id string) (hash string, err error) {
queryStmt := fmt.Sprintf(`SELECT hash
FROM event
WHERE id = '%s'`, id)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
if rows.Next() {
err = rows.Scan(hash)
if err != nil {
return
}
}
return
}
func ReadRoomMembers(roomId string) (roomMembers []string, err error) {
queryStmt := fmt.Sprintf(`SELECT userId
FROM roomMember
WHERE roomId = '%s'`, roomId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
roomMembers = []string{}
for rows.Next() {
var foundUser string
err = rows.Scan(&foundUser)
if err != nil {
return
}
roomMembers = append(roomMembers, foundUser)
}
return
}
func ReadServers(roomId string) (servers []string, err error) {
queryStmt := fmt.Sprintf(`SELECT DISTINCT server
FROM roomMember
WHERE roomId = '%s'`, roomId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
servers = []string{}
for rows.Next() {
var foundUser string
err = rows.Scan(&foundUser)
if err != nil {
return
}
servers = append(servers, foundUser)
}
return
}
func ReadParents(eventId string) (parents map[string]EventHash, err error) {
queryStmt := fmt.Sprintf(`SELECT e.id, e.hash
FROM event as e
join parent as p on e.id = p.parentId
WHERE p.eventId = '%s'`, eventId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
parents = make(map[string]EventHash)
for rows.Next() {
var eventId string
var foundEvent EventHash
err = rows.Scan(&eventId,
&foundEvent.SHA256,
)
if err != nil {
return
}
parents[eventId] = foundEvent
}
return
}
func ReadEventsWithoutChild(roomId string) (events map[string]EventHash, depth int, err error) {
queryStmt := fmt.Sprintf(`SELECT e.id, e.hash, e.depth
FROM event as e
LEFT JOIN parent as p on e.id = p.parentId
WHERE p.eventId IS NULL AND e.roomId = '%s'`, roomId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
events = make(map[string]EventHash)
for rows.Next() {
var eventId string
var foundEvent EventHash
var foundDepth int
err = rows.Scan(&eventId,
&foundEvent.SHA256,
&foundDepth,
)
if foundDepth > depth {
depth = foundDepth
}
if err != nil {
return
}
events[eventId] = foundEvent
}
return
}
func ReadAuthEvents(eventId string) (authEvents map[string]EventHash, err error) {
queryStmt := fmt.Sprintf(`SELECT e.id, e.hash
FROM event as e
join authEvent as a on e.id = a.authEventId
WHERE a.eventId = '%s'`, eventId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
authEvents = make(map[string]EventHash)
for rows.Next() {
var eventId string
var foundEvent EventHash
err = rows.Scan(&eventId,
&foundEvent.SHA256,
)
if err != nil {
return
}
authEvents[eventId] = foundEvent
}
return
}
func ReadEvent(id string) (foundEvent *Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, eventType, content, parentId, depth
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE id = '%s'`, id)
@ -73,13 +398,34 @@ func ReadEvent(id string) (foundEvent *Event, err error) {
if rows.Next() {
foundEvent = &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.ParentId,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
@ -88,8 +434,120 @@ func ReadEvent(id string) (foundEvent *Event, err error) {
return
}
func ReadStateEvent(roomId string, eventType string, stateKey string) (foundEvent *Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE roomId = '%s'
AND eventType = '%s'
AND stateKey = '%s'`, roomId, eventType, stateKey)
if stateKey == "" {
queryStmt = fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE roomId = '%s'
AND eventType = '%s'`, roomId, eventType)
}
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
if rows.Next() {
foundEvent = &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
}
return
}
func ReadStateEvents(roomId string, eventType string) (foundEvents []*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE roomId = '%s'
AND eventType = '%s'`, roomId, eventType)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
foundEvent := &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
foundEvents = append(foundEvents, foundEvent)
}
return
}
func ReadEventsFromRoom(roomId string) (events map[string]*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, eventType, content, parentId, depth
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE roomId = '%s'`, roomId)
@ -104,24 +562,98 @@ func ReadEventsFromRoom(roomId string) (events map[string]*Event, err error) {
for rows.Next() {
foundEvent := &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.ParentId,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
events[foundEvent.Id] = foundEvent
}
return
}
func ReadEventsFromTransaction(txnId string) (events map[string]*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, eventType, content, parentId, depth
func ReadStateEventsFromRoom(roomId string) (events []*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE eventType <> 'm.room.message' AND roomId = '%s'`, roomId)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
for rows.Next() {
foundEvent := &Event{}
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
events = append(events, foundEvent)
}
return
}
func ReadEventsFromTransaction(txnId string) (events []*Event, err error) {
queryStmt := fmt.Sprintf(`SELECT id, roomId, txnId, sender, origin, timestamp, eventType, content, depth, hash, signature
FROM event
WHERE txnId = '%s'`, txnId)
@ -132,22 +664,41 @@ func ReadEventsFromTransaction(txnId string) (events map[string]*Event, err erro
defer rows.Close()
events = make(map[string]*Event)
for rows.Next() {
foundEvent := &Event{}
err = rows.Scan(
&foundEvent.Id,
var signature string
err = rows.Scan(&foundEvent.Id,
&foundEvent.RoomId,
&foundEvent.Unsigned.TransactionId,
&foundEvent.Sender,
&foundEvent.Origin,
&foundEvent.Timestamp,
&foundEvent.EventType,
&foundEvent.Content,
&foundEvent.ParentId,
&foundEvent.Depth,
&foundEvent.Hashes.SHA256,
&signature,
)
foundEvent.Signatures = make(map[string]map[string]string)
foundEvent.Signatures[foundEvent.Origin] = make(map[string]string)
foundEvent.Signatures[foundEvent.Origin]["ed25519:1"] = signature
if err != nil {
return
}
events[foundEvent.Id] = foundEvent
foundEvent.PrevEventHashes, err = ReadParents(foundEvent.Id)
if err != nil {
return
}
foundEvent.AuthEventHashes, err = ReadAuthEvents(foundEvent.Id)
if err != nil {
return
}
events = append(events, foundEvent)
}
return
@ -155,11 +706,8 @@ func ReadEventsFromTransaction(txnId string) (events map[string]*Event, err erro
func UpdateEvent(event *Event) (err error) {
sqlStmt := fmt.Sprintf(`UPDATE event SET
roomId = ?,
eventType = ?,
content = ?,
parentId = ?,
depth = ?
WHERE id = ?`)
tx, err := database.DB.Begin()
@ -174,11 +722,8 @@ func UpdateEvent(event *Event) (err error) {
defer stmt.Close()
_, err = stmt.Exec(
event.RoomId,
event.EventType,
event.Content,
event.ParentId,
event.Depth,
event.Id,
)
if err != nil {
@ -203,6 +748,52 @@ func DeleteEvent(id string) (err error) {
return
}
err = DeleteParents(id)
if err != nil {
return
}
err = DeleteAuthEvents(id)
if err != nil {
return
}
tx.Commit()
return
}
func DeleteParents(eventId string) (err error) {
queryStmt := fmt.Sprintf(`DELETE FROM parent
WHERE eventId = '%s'`, eventId)
tx, err := database.DB.Begin()
if err != nil {
return
}
_, err = database.DB.Exec(queryStmt)
if err != nil {
return
}
tx.Commit()
return
}
func DeleteAuthEvents(eventId string) (err error) {
queryStmt := fmt.Sprintf(`DELETE FROM authEvent
WHERE eventId = '%s'`, eventId)
tx, err := database.DB.Begin()
if err != nil {
return
}
_, err = database.DB.Exec(queryStmt)
if err != nil {
return
}
tx.Commit()
return
}

View file

@ -0,0 +1,8 @@
package event
type Transaction struct {
Id string `json:"id,omitempty"`
Origin string `json:"origin,omitempty"`
Timestamp int64 `json:"timestamp,omitempty"`
PDUS []*Event `json:"pdus,omitempty"`
}

View file

@ -0,0 +1,100 @@
package event
import (
"fmt"
"nutfactory.org/Matrix/utils/database"
)
func CreateTransaction(transaction *Transaction) (err error) {
sqlStmt := fmt.Sprintf(`INSERT INTO txn
(id, origin, timestamp)
VALUES
(?, ?, ?)`)
tx, err := database.DB.Begin()
if err != nil {
return
}
stmt, err := tx.Prepare(sqlStmt)
if err != nil {
return
}
defer stmt.Close()
_, err = stmt.Exec(transaction.Id, transaction.Origin, transaction.Timestamp)
if err != nil {
return
}
tx.Commit()
return
}
func ReadTransaction(id string) (foundTransaction *Transaction, err error) {
queryStmt := fmt.Sprintf(`SELECT id, origin, timestamp
FROM txn
WHERE id = '%s'`, id)
rows, err := database.DB.Query(queryStmt)
if err != nil {
return
}
defer rows.Close()
if rows.Next() {
foundTransaction = &Transaction{}
err = rows.Scan(&foundTransaction.Id, &foundTransaction.Origin, &foundTransaction.Timestamp)
if err != nil {
return
}
foundTransaction.PDUS, err = ReadEventsFromTransaction(foundTransaction.Id)
}
return
}
func UpdateTransaction(transaction *Transaction) (err error) {
sqlStmt := fmt.Sprintf(`UPDATE txn SET
origin = ?,
timestamp = ?
WHERE id = ?`)
tx, err := database.DB.Begin()
if err != nil {
return
}
stmt, err := tx.Prepare(sqlStmt)
if err != nil {
return
}
defer stmt.Close()
_, err = stmt.Exec(transaction.Origin, transaction.Timestamp, transaction.Id)
if err != nil {
return
}
tx.Commit()
return
}
func DeleteTransaction(id string) (err error) {
queryStmt := fmt.Sprintf(`DELETE FROM txn
WHERE id = '%s'`, id)
tx, err := database.DB.Begin()
if err != nil {
return
}
_, err = database.DB.Exec(queryStmt)
if err != nil {
return
}
tx.Commit()
return
}