Skip to content

Latest commit

 

History

History
150 lines (130 loc) · 3.43 KB

File metadata and controls

150 lines (130 loc) · 3.43 KB

package main

import ( "fmt" "sync" )

type AccountState interface { GetAccount(name string) AccountValue }

type AccountValue struct { Name string Balance int64 }

type AccountUpdate struct { Name string BalanceChange int }

type Transaction interface { Updates(state AccountState) ([]AccountUpdate, error) }

type Block struct { Transactions []Transaction }

type AccountDB struct { mu sync.RWMutex accounts map[string]int64 }

func (db *AccountDB) GetAccount(name string) AccountValue { db.mu.RLock() defer db.mu.RUnlock() balance, exists := db.accounts[name] if !exists { return AccountValue{Name: name, Balance: 0} } return AccountValue{Name: name, Balance: balance} }

func (db *AccountDB) ApplyUpdate(update AccountUpdate, locks map[string]*sync.Mutex) { locks[update.Name].Lock() defer locks[update.Name].Unlock() db.mu.Lock() defer db.mu.Unlock()

currentBalance, exists := db.accounts[update.Name]
if !exists {
	currentBalance = 0
}
newBalance := int(currentBalance) + update.BalanceChange
if newBalance < 0 {
	return // Ignore updates that would cause negative balance
}
db.accounts[update.Name] = int64(newBalance)

}

func ExecuteBlock(block Block) ([]AccountValue, error) { accountDB := &AccountDB{accounts: make(map[string]int64)} accountLocks := make(map[string]*sync.Mutex) var accountLocksMu sync.Mutex

// Initialize account balances for demonstration purposes (if needed)
accountDB.accounts["A"] = 100 // Assuming "A" starts with 100 for transaction to be valid

for _, tx := range block.Transactions {
	updates, _ := tx.Updates(accountDB)
	for _, update := range updates {
		accountLocksMu.Lock()
		if _, exists := accountLocks[update.Name]; !exists {
			accountLocks[update.Name] = &sync.Mutex{}
		}
		accountLocksMu.Unlock()
	}
}

var wg sync.WaitGroup
txChan := make(chan Transaction)
const N = 4 // Number of workers
for i := 0; i < N; i++ {
	go func() {
		for tx := range txChan {
			updates, err := tx.Updates(accountDB)
			if err == nil {
				for _, update := range updates {
					fmt.Printf("Applying update: %v\n", update)
					accountDB.ApplyUpdate(update, accountLocks)
				}
			} else {
				fmt.Printf("Error processing transaction: %v\n", err)
			}
			wg.Done()
		}
	}()
}

for _, tx := range block.Transactions {
	wg.Add(1)
	txChan <- tx
}
close(txChan)
wg.Wait()

var finalState []AccountValue
for name, balance := range accountDB.accounts {
	finalState = append(finalState, AccountValue{Name: name, Balance: balance})
	fmt.Printf("Final Account: %s, Balance: %d\n", name, balance) // Debug final balances
}
return finalState, nil

}

type TransferTransaction struct { from string to string value int }

func (t TransferTransaction) Updates(state AccountState) ([]AccountUpdate, error) { fromAcc := state.GetAccount(t.from) if fromAcc.Balance < int64(t.value) { return nil, fmt.Errorf("insufficient balance") } return []AccountUpdate{ {Name: t.from, BalanceChange: -t.value}, {Name: t.to, BalanceChange: t.value}, }, nil }

func main() { block := Block{ Transactions: []Transaction{ TransferTransaction{from: "A", to: "B", value: 5}, TransferTransaction{from: "B", to: "C", value: 10}, TransferTransaction{from: "B", to: "C", value: 30}, }, } finalState, err := ExecuteBlock(block) if err != nil { fmt.Println("Error:", err) return }

for _, acc := range finalState {
	fmt.Printf("Account: %s, Balance: %d\n", acc.Name, acc.Balance)
}

}