blockchain sandbox: UTXO chain, p2p mining, attacker node
This commit is contained in:
@@ -0,0 +1,123 @@
|
||||
package chain
|
||||
|
||||
import (
|
||||
"crypto/ed25519"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// CoinSpend references a coin being spent, with proof of ownership.
|
||||
type CoinSpend struct {
|
||||
CoinID CoinID
|
||||
Owner ed25519.PublicKey
|
||||
Signature []byte
|
||||
}
|
||||
|
||||
type Transaction struct {
|
||||
Spends []CoinSpend
|
||||
Coins []Coin
|
||||
|
||||
cachedID string
|
||||
}
|
||||
|
||||
func (tx *Transaction) IsCoinbase() bool {
|
||||
return len(tx.Spends) == 0
|
||||
}
|
||||
|
||||
func (tx *Transaction) TxID() string {
|
||||
if tx.cachedID != "" {
|
||||
return tx.cachedID
|
||||
}
|
||||
|
||||
var b strings.Builder
|
||||
for _, s := range tx.Spends {
|
||||
b.WriteString(s.CoinID.TxHash)
|
||||
b.WriteString(strconv.Itoa(s.CoinID.Index))
|
||||
}
|
||||
for _, c := range tx.Coins {
|
||||
b.Write(c.Owner)
|
||||
b.WriteString(strconv.Itoa(c.Amount))
|
||||
}
|
||||
h := sha256.Sum256([]byte(b.String()))
|
||||
tx.cachedID = hex.EncodeToString(h[:])
|
||||
return tx.cachedID
|
||||
}
|
||||
|
||||
func (tx *Transaction) Verify() bool {
|
||||
if tx.IsCoinbase() {
|
||||
return true
|
||||
}
|
||||
|
||||
hash := []byte(tx.TxID())
|
||||
for _, s := range tx.Spends {
|
||||
if s.Owner == nil {
|
||||
return false
|
||||
}
|
||||
if !ed25519.Verify(s.Owner, hash, s.Signature) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (tx *Transaction) String() string {
|
||||
if tx.IsCoinbase() {
|
||||
return fmt.Sprintf("COINBASE -> %s: %d", tx.Coins[0].OwnerHex()[:7], tx.Coins[0].Amount)
|
||||
}
|
||||
|
||||
from := hex.EncodeToString(tx.Spends[0].Owner)[:7]
|
||||
to := tx.Coins[0].OwnerHex()[:7]
|
||||
|
||||
return fmt.Sprintf("%s -> %s: %d", from, to, tx.Coins[0].Amount)
|
||||
}
|
||||
|
||||
func NewCoinbaseTransaction(to ed25519.PublicKey, amount int) *Transaction {
|
||||
return &Transaction{
|
||||
Coins: []Coin{{Amount: amount, Owner: to}},
|
||||
}
|
||||
}
|
||||
|
||||
func NewTransaction(wallet *Wallet, to ed25519.PublicKey, amount int, unspent []UnspentCoin) *Transaction {
|
||||
var selected []UnspentCoin
|
||||
var total int
|
||||
|
||||
for _, u := range unspent {
|
||||
selected = append(selected, u)
|
||||
total += u.Amount
|
||||
if total >= amount {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if total < amount {
|
||||
return nil
|
||||
}
|
||||
|
||||
coins := []Coin{{Amount: amount, Owner: to}}
|
||||
|
||||
change := total - amount
|
||||
|
||||
if change > 0 {
|
||||
coins = append(coins, Coin{Amount: change, Owner: wallet.PublicKey})
|
||||
}
|
||||
|
||||
spends := make([]CoinSpend, len(selected))
|
||||
for i, u := range selected {
|
||||
spends[i] = CoinSpend{
|
||||
CoinID: u.CoinID,
|
||||
Owner: wallet.PublicKey,
|
||||
}
|
||||
}
|
||||
|
||||
tx := &Transaction{Spends: spends, Coins: coins}
|
||||
hash := []byte(tx.TxID())
|
||||
for i := range tx.Spends {
|
||||
tx.Spends[i].Signature = ed25519.Sign(wallet.PrivateKey, hash)
|
||||
}
|
||||
|
||||
return tx
|
||||
}
|
||||
Reference in New Issue
Block a user