diff --git a/.gitignore b/.gitignore index e69de29b..37e2c0b8 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +/*.txt \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..89efc70f --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,9 @@ +{ + "cSpell.words": [ + "Merkle" + ], + "cSpell.ignoreWords": [ + "lnode", + "rnode" + ] +} \ No newline at end of file diff --git a/Prioritize.go b/Prioritize.go new file mode 100644 index 00000000..90bdd473 --- /dev/null +++ b/Prioritize.go @@ -0,0 +1,61 @@ +package main + +import ( + "encoding/hex" + "encoding/json" + "os" + "sort" +) + +type TxInfo struct { + TxID string + WTxID string + Fee uint64 + Weight uint64 +} + +func comp(a, b TxInfo) bool { + return float64(a.Fee)/float64(a.Weight) > float64(b.Fee)/float64(b.Weight) +} +func Prioritize() (uint64, []string, []string) { + var permittedTxIDs []string + var permittedWTxIDs []string + dir := "./mempool" + files, _ := os.ReadDir(dir) + var txInfo []TxInfo + for _, file := range files { + txData, err := jsonData(dir + "/" + file.Name()) + Handle(err) + var tx Transaction + err = json.Unmarshal([]byte(txData), &tx) + var fee uint64 = 0 + for _, vin := range tx.Vin { + fee += vin.Prevout.Value + } + for _, vout := range tx.Vout { + fee -= vout.Value + } + serialized, _ := serializeTransaction(&tx) + segserialized, _ := SegWitSerialize(&tx) + txID := to_sha(to_sha(serialized)) + wtxID := to_sha(to_sha(segserialized)) + txInfo = append(txInfo, TxInfo{TxID: hex.EncodeToString(txID), WTxID: hex.EncodeToString(wtxID), Fee: fee, Weight: uint64(calculateWitnessSize(&tx) + CalculateBaseSize(&tx)*4)}) + + } + sort.Slice(txInfo, func(i, j int) bool { + return comp(txInfo[i], txInfo[j]) + }) + var PermissibleTxs []TxInfo + var PermissibleWeight uint64 = 4000000 + var reward uint64 = 0 + for _, tx := range txInfo { + if PermissibleWeight >= tx.Weight { + PermissibleTxs = append(PermissibleTxs, tx) + PermissibleWeight -= tx.Weight + permittedTxIDs = append(permittedTxIDs, tx.TxID) + permittedWTxIDs = append(permittedWTxIDs, tx.WTxID) + reward += tx.Fee + } + } + return reward, permittedTxIDs, permittedWTxIDs +} diff --git a/address.go b/address.go new file mode 100644 index 00000000..6924350b --- /dev/null +++ b/address.go @@ -0,0 +1,150 @@ +package main + +import ( + "encoding/json" + "fmt" + "os" +) + +var ( + ct_p2pkh = 0 + ct_p2sh = 0 + ct_p2wpkh = 0 + ct_p2wsh = 0 +) + +func Address() { + dir := "./mempool" + files, err := os.ReadDir(dir) + Handle(err) + for _, file := range files { + txData, err := jsonData(dir + "/" + file.Name()) + Handle(err) + var tx Transaction + err = json.Unmarshal([]byte(txData), &tx) + Handle(err) + for _, vin := range tx.Vin { + if vin.Prevout.ScriptpubkeyType == "p2pkh" { + pubkey_asm := vin.Prevout.ScriptpubkeyAsm + address := p2pkh(pubkey_asm) + if string(address) == vin.Prevout.ScriptpubkeyAddress { + // fmt.Println(vin.Prevout.ScriptpubkeyAddress) + // fmt.Printf(" %s\n", address) + ct_p2pkh++ + continue + } else { + fmt.Println("Address not matched") + fmt.Println("Address: ", address) + fmt.Println("Scriptpubkey Address: ", vin.Prevout.ScriptpubkeyAddress) + } + } + + if vin.Prevout.ScriptpubkeyType == "p2sh" { + pubkey_asm := vin.Prevout.ScriptpubkeyAsm + address := p2sh(pubkey_asm) + if string(address) == vin.Prevout.ScriptpubkeyAddress { + // fmt.Println(vin.Prevout.ScriptpubkeyAddress) + // fmt.Printf(" %s\n", address) + ct_p2sh++ + continue + } else { + fmt.Println("Address not matched") + fmt.Println("Address: ", address) + fmt.Println("Scriptpubkey Address: ", vin.Prevout.ScriptpubkeyAddress) + } + } + + if vin.Prevout.ScriptpubkeyType == "v0_p2wpkh" { + pubkey_asm := vin.Prevout.ScriptpubkeyAsm + address := p2wpkh(pubkey_asm) + if string(address) == vin.Prevout.ScriptpubkeyAddress { + // fmt.Println(vin.Prevout.ScriptpubkeyAddress) + // fmt.Printf(" %s\n", address) + ct_p2wpkh++ + continue + } else { + fmt.Println("Address not matched") + fmt.Println("Address: ", address) + fmt.Println("Scriptpubkey Address: ", vin.Prevout.ScriptpubkeyAddress) + } + } + + if vin.Prevout.ScriptpubkeyType == "v0_p2wsh" { + pubkey_asm := vin.Prevout.ScriptpubkeyAsm + address := p2wsh(pubkey_asm) + if string(address) == vin.Prevout.ScriptpubkeyAddress { + // fmt.Println(vin.Prevout.ScriptpubkeyAddress) + // fmt.Printf(" %s\n", address) + ct_p2wsh++ + continue + } else { + fmt.Println("Address not matched") + fmt.Println("Address: ", address) + fmt.Println("Scriptpubkey Address: ", vin.Prevout.ScriptpubkeyAddress) + } + } + } + for _, vout := range tx.Vout { + if vout.ScriptpubkeyType == "p2pkh" { + pubkey_asm := vout.ScriptpubkeyAsm + address := p2pkh(pubkey_asm) + if string(address) == vout.ScriptpubkeyAddress { + // fmt.Println(vout.ScriptpubkeyAddress) + // fmt.Printf(" %s\n", address) + ct_p2pkh++ + } else { + fmt.Println("Address not matched") + fmt.Println("Address: ", address) + fmt.Println("Scriptpubkey Address: ", vout.ScriptpubkeyAddress) + } + } + + if vout.ScriptpubkeyType == "p2sh" { + pubkey_asm := vout.ScriptpubkeyAsm + address := p2sh(pubkey_asm) + if string(address) == vout.ScriptpubkeyAddress { + // fmt.Println(vout.ScriptpubkeyAddress) + // fmt.Printf(" %s\n", address) + ct_p2sh++ + continue + } else { + fmt.Println("Address not matched") + fmt.Println("Address: ", address) + fmt.Println("Scriptpubkey Address: ", vout.ScriptpubkeyAddress) + } + } + + if vout.ScriptpubkeyType == "v0_p2wpkh" { + pubkey_asm := vout.ScriptpubkeyAsm + address := p2wpkh(pubkey_asm) + if string(address) == vout.ScriptpubkeyAddress { + // fmt.Println(vout.ScriptpubkeyAddress) + // fmt.Printf(" %s\n", address) + ct_p2wpkh++ + } else { + fmt.Println("Address not matched") + fmt.Printf("Address: %s\n", address) + fmt.Println("Scriptpubkey Address: ", vout.ScriptpubkeyAddress) + } + } + + if vout.ScriptpubkeyType == "v0_p2wsh" { + pubkey_asm := vout.ScriptpubkeyAsm + address := p2wsh(pubkey_asm) + if string(address) == vout.ScriptpubkeyAddress { + // fmt.Println(vout.ScriptpubkeyAddress) + // fmt.Printf(" %s\n", address) + ct_p2wsh++ + } else { + fmt.Println("Address not matched") + fmt.Printf("Address: %s\n", address) + fmt.Println("Scriptpubkey Address: ", vout.ScriptpubkeyAddress) + } + } + } + } + fmt.Println("Count of p2pkh address matched: ", ct_p2pkh) + fmt.Println("Count of p2sh address matched: ", ct_p2sh) + fmt.Println("Count of p2wpkh address matched: ", ct_p2wpkh) + fmt.Println("Count of p2wpkh address matched: ", ct_p2wsh) +} diff --git a/coinbase.go b/coinbase.go new file mode 100644 index 00000000..1643d0df --- /dev/null +++ b/coinbase.go @@ -0,0 +1,43 @@ +package main + +func Coinbase(netReward uint64) *Transaction { + witnessCommitment := CreateWitnessMerkle() + coinbaseTx := Transaction{ + Version: 1, + Vin: []Input{ + { + TxID: "0000000000000000000000000000000000000000000000000000000000000000", + Vout: 0xffffffff, + Prevout: Prevout{ + Scriptpubkey: "0014df4bf9f3621073202be59ae590f55f42879a21a0", + ScriptpubkeyAsm: "0014df4bf9f3621073202be59ae590f55f42879a21a0", + ScriptpubkeyType: "p2pkh", + ScriptpubkeyAddress: "bc1qma9lnumzzpejq2l9ntjepa2lg2re5gdqn3nf0c", + Value: uint64(netReward), + }, + IsCoinbase: true, + Sequence: 0xffffffff, + Scriptsig: "03951a0604f15ccf5609013803062b9b5a0100072f425443432f20", + Witness: []string{"0000000000000000000000000000000000000000000000000000000000000000"}, + }, + }, + Vout: []Prevout{ + { + Scriptpubkey: "0014df4bf9f3621073202be59ae590f55f42879a21a0", + ScriptpubkeyAsm: "0014df4bf9f3621073202be59ae590f55f42879a21a0", + ScriptpubkeyType: "p2pkh", + ScriptpubkeyAddress: "bc1qma9lnumzzpejq2l9ntjepa2lg2re5gdqn3nf0c", + Value: uint64(netReward), + }, + { + Scriptpubkey: "6a24" + "aa21a9ed" + witnessCommitment, //OPRETURN +OP_PUSHBYTES_36+ commitment header + witnessCommitment + ScriptpubkeyAsm: "OP_RETURN" + "OP_PUSHBYTES_36" + "aa21a9ed" + witnessCommitment, + ScriptpubkeyType: "op_return", + ScriptpubkeyAddress: "bc1qma9lnumzzpejq2l9ntjepa2lg2re5gdqn3nf0c", + Value: uint64(0), + }, + }, + Locktime: 0, + } + return &coinbaseTx +} diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..2df78c6e --- /dev/null +++ b/go.mod @@ -0,0 +1,8 @@ +module github.com/pred695/code-challenge-2024-pred695 + +go 1.22.0 + +require ( + github.com/btcsuite/btcutil v1.0.2 + github.com/mr-tron/base58 v1.2.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..b9d3a11b --- /dev/null +++ b/go.sum @@ -0,0 +1,37 @@ +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.2 h1:9iZ1Terx9fMIOtq1VrwdqfsATL9MC2l8ZrUY6YZ2uts= +github.com/btcsuite/btcutil v1.0.2/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go new file mode 100644 index 00000000..bd23ba9f --- /dev/null +++ b/main.go @@ -0,0 +1,48 @@ +package main + +import ( + "encoding/hex" + "os" + "time" +) + +type BlockHeader struct { + version uint32 + prevBlockHash string + merkleRoot string + time int64 + bits string + nonce uint32 +} + +var Bh BlockHeader = BlockHeader{ + version: 7, + prevBlockHash: "", + merkleRoot: "", + time: time.Now().Unix(), + bits: "1d00ffff", + nonce: 0, +} + +func main() { + netReward, TxIDs, _ := Prioritize() + + cbTx := Coinbase(netReward) + serializedcbTx, _ := serializeTransaction(cbTx) + TxIDs = append([]string{hex.EncodeToString(to_sha(to_sha(serializedcbTx)))}, TxIDs...) + mkr := NewMerkleTree(TxIDs) + Bh.merkleRoot = hex.EncodeToString(mkr.Data) + + if ProofOfWork(&Bh) { + file, _ := os.Create("output.txt") + defer file.Close() + + serializedBh := SerializeBlockHeader(&Bh) + file.WriteString(hex.EncodeToString(serializedBh) + "\n") + file.WriteString(hex.EncodeToString(serializedcbTx) + "\n") + for _, tx := range TxIDs { + file.WriteString(tx + "\n") + } + } + +} diff --git a/merkle.backup b/merkle.backup new file mode 100644 index 00000000..954309e5 --- /dev/null +++ b/merkle.backup @@ -0,0 +1,179 @@ +package main + +import ( + "crypto/sha256" + "encoding/hex" + "fmt" + "os" + "strings" +) + +// func hash256(input string) string { +// fmt.Println("txid: ", input) +// h1 := sha256.Sum256([]byte(input)) +// fmt.Printf("h1: %x\n", h1) +// h2 := sha256.Sum256(h1[:]) +// fmt.Printf("h2: %x\n", h2) + +// return hex.EncodeToString(h2[:]) +// } + +func hash256(data string) string { + // Decode hexadecimal string to byte slice + rawBytes, _ := hex.DecodeString(data) + + // First SHA256 hash + hash1 := sha256.Sum256(rawBytes) + + // Second SHA256 hash + hash2 := sha256.Sum256(hash1[:]) + + // Convert hash2 to hexadecimal string + hashedString := hex.EncodeToString(hash2[:]) + + return hashedString +} + +func generateMerkleRoot(txids []string) string { + if len(txids) == 0 { + return "" + } + + level := make([]string, len(txids)) + copy(level, txids) + + for len(level) > 1 { + nextLevel := make([]string, 0) + + for i := 0; i < len(level); i += 2 { + var pairHash string + if i+1 == len(level) { + pairHash = hash256(level[i] + level[i]) + } else { + pairHash = hash256(level[i] + level[i+1]) + } + nextLevel = append(nextLevel, pairHash) + } + + level = nextLevel + } + + fmt.Println("Computed Merkle Root:", level[0]) + return level[0] +} + +func CreateMerkleTree() { + fileName := "test.txt" + + // Read all lines from the file + data, err := os.ReadFile(fileName) + if err != nil { + fmt.Println("Error reading file:", err) + return + } + + // Split lines into an array of strings (assuming each line is a txid)(reversed) + txids := strings.Split(string(data), "\n") + + // Call generateMerkleRoot function with txids from the file + merkleRoot := generateMerkleRoot(txids) + + // Print the computed Merkle root + fmt.Println("Computed Merkle Root:", merkleRoot) +} + + +mine + +package main + +import ( + "encoding/hex" + "fmt" + "os" + "strings" +) + +type MerkleNode struct { + Left *MerkleNode + Data []byte + Right *MerkleNode +} + +type MerkleTree struct { + MerkleRoot *MerkleNode +} + +func NewMerkleNode(lnode *MerkleNode, rnode *MerkleNode, data []byte) *MerkleNode { + var mNode MerkleNode = MerkleNode{} + if lnode == nil && rnode == nil { + //hash256 of the data + mNode.Data = reverseBytes(data) + } else { + var prevHash []byte = append(lnode.Data, rnode.Data...) + mNode.Data = (to_sha(to_sha(prevHash))) + } + mNode.Left = lnode + mNode.Right = rnode + return &mNode +} + +func NewMerkleTree(leaves []string) *MerkleNode { + var nodes []MerkleNode + + for _, leaf := range leaves { + data, _ := hex.DecodeString(leaf) + var node MerkleNode = *NewMerkleNode(nil, nil, data) + nodes = append(nodes, node) + } + + for len(nodes) > 1 { + var newLevel []MerkleNode + for i := 0; i < len(nodes); i += 2 { + // Handle case where the total number of nodes is odd. + if len(nodes)%2 != 0 { + nodes = append(nodes, nodes[len(nodes)-1]) + } + node := *NewMerkleNode(&nodes[i], &nodes[i+1], nil) + newLevel = append(newLevel, node) + } + nodes = newLevel + } + return &nodes[0] + +} + +// func ReadTransactions(dir string) ([][]byte, error) { +// var Txs [][]byte +// files, err := os.ReadDir(dir) +// Handle(err) +// for _, file := range files { +// txData, err := jsonData(dir + "/" + file.Name()) +// Handle(err) +// var tx Transaction +// err = json.Unmarshal([]byte(txData), &tx) +// Handle(err) +// serialized, err := serializeTransaction(&tx) +// Txs = append(Txs, (to_sha(to_sha(serialized)))) //appending the txid in little endian +// } +// return Txs, nil +// } + +func CreateMerkleTree() { + fileName := "test.txt" + + // Read all lines from the file + data, err := os.ReadFile(fileName) + if err != nil { + fmt.Println("Error reading file:", err) + return + } + + // Split lines into an array of strings (assuming each line is a txid)(reversed) + txids := strings.Split(string(data), "\n") + + // Calculate the merkle root: + merkleRoot := NewMerkleTree(txids) + + fmt.Printf("merkle root; %x\n", merkleRoot.Data) +} diff --git a/merkle.go b/merkle.go new file mode 100644 index 00000000..0c3bfa27 --- /dev/null +++ b/merkle.go @@ -0,0 +1,54 @@ +package main + +import ( + "encoding/hex" +) + +type MerkleNode struct { + Left *MerkleNode + Data []byte + Right *MerkleNode +} + +type MerkleTree struct { + MerkleRoot *MerkleNode +} + +func NewMerkleNode(lnode *MerkleNode, rnode *MerkleNode, data []byte) *MerkleNode { + var mNode MerkleNode = MerkleNode{} + if lnode == nil && rnode == nil { + //hash256 of the data + mNode.Data = reverseBytes(data) + } else { + var prevHash []byte = append(lnode.Data, rnode.Data...) + mNode.Data = (to_sha(to_sha(prevHash))) + } + mNode.Left = lnode + mNode.Right = rnode + return &mNode +} + +func NewMerkleTree(leaves []string) *MerkleNode { + var nodes []MerkleNode + + for _, leaf := range leaves { + data, _ := hex.DecodeString(leaf) + var node MerkleNode = *NewMerkleNode(nil, nil, data) + nodes = append(nodes, node) + } + + for len(nodes) > 1 { + var newLevel []MerkleNode + for i := 0; i < len(nodes); i += 2 { + // Handle case where the total number of nodes is odd. + if len(nodes)%2 != 0 { + nodes = append(nodes, nodes[len(nodes)-1]) + } + node := *NewMerkleNode(&nodes[i], &nodes[i+1], nil) + newLevel = append(newLevel, node) + } + nodes = newLevel + } + return &nodes[0] + +} \ No newline at end of file diff --git a/p2pkh.go b/p2pkh.go new file mode 100644 index 00000000..67154ca5 --- /dev/null +++ b/p2pkh.go @@ -0,0 +1,56 @@ +package main + +import ( + "encoding/hex" + "strings" + + "github.com/mr-tron/base58" +) + +/* +The example: + + { + "scriptpubkey": "76a9146085312a9c500ff9cc35b571b0a1e5efb7fb9f1688ac", + "scriptpubkey_asm": "OP_DUP OP_HASH160 OP_PUSHBYTES_20 6085312a9c500ff9cc35b571b0a1e5efb7fb9f16 OP_EQUALVERIFY OP_CHECKSIG", + "scriptpubkey_type": "p2pkh", + "scriptpubkey_address": "19oMRmCWMYuhnP5W61ABrjjxHc6RphZh11", + "value": 100000 + } +*/ +const ( + versionByte string = "00" +) + +func ExtractHexFromScriptpubkeyAsm(str []string) string { + for i := 0; i < len(str); i++ { + if str[i] == "OP_PUSHBYTES_20" || str[i] == "OP_PUSHBYTES_32" { + return str[i+1] + } + } + return "" +} + +func Base58Encode(input []byte) []byte { + var encoded string = base58.Encode(input) + return []byte(encoded) +} +func p2pkh(scriptpubkey_asm string) []byte { + str := strings.Split(scriptpubkey_asm, " ") + + pubkeyhash := ExtractHexFromScriptpubkeyAsm(str) + // Convert hex to bytes) + pubkeyhash_bytes, _ := hex.DecodeString(pubkeyhash) + versionByte_bytes, _ := hex.DecodeString(versionByte) + + version_pubkeyhash := append(versionByte_bytes, pubkeyhash_bytes...) + + checksum := to_sha(to_sha(version_pubkeyhash)) + + appended_checksum := append(version_pubkeyhash, checksum[:4]...) + + address := Base58Encode(appended_checksum) + + return address + +} diff --git a/p2sh.go b/p2sh.go new file mode 100644 index 00000000..8932243b --- /dev/null +++ b/p2sh.go @@ -0,0 +1,31 @@ +/* + { + "scriptpubkey": "a914d6bfee7c8c925ad5ff17035b2672eee12baf168b87", + "scriptpubkey_asm": "OP_HASH160 OP_PUSHBYTES_20 d6bfee7c8c925ad5ff17035b2672eee12baf168b OP_EQUAL", + "scriptpubkey_type": "p2sh", + "scriptpubkey_address": "3MGWTEe7C77RBmhtGhYN1B3fszdXYSQdZA", + "value": 2313802 + } +*/ +package main + +import ( + "encoding/hex" + "strings" +) + +func p2sh(scriptpubkey_asm string) []byte { + hashed_script := ExtractHexFromScriptpubkeyAsm(strings.Split(scriptpubkey_asm, " ")) + hashed_script_bytes, _ := hex.DecodeString(hashed_script) + versionByte_bytes, _ := hex.DecodeString("05") + version_hash := append(versionByte_bytes, hashed_script_bytes...) + + checksum := to_sha(to_sha(version_hash)) + + appended_checksum := append(version_hash, checksum[:4]...) + + address := Base58Encode(appended_checksum) + + return address + +} diff --git a/p2wpkh.go b/p2wpkh.go new file mode 100644 index 00000000..35bc4bc7 --- /dev/null +++ b/p2wpkh.go @@ -0,0 +1,36 @@ +package main + +import ( + "encoding/hex" + "strings" + + "github.com/btcsuite/btcutil/bech32" +) + +/* + { + "scriptpubkey": " ", + "scriptpubkey_asm": "OP_0 OP_PUSHBYTES_20 ce0b5aa0dac72397a8aa5a85d90a2cfb46f0f630", + "scriptpubkey_type": "v0_p2wpkh", + "scriptpubkey_address": "bc1qec944gx6cu3e0292t2zajz3vldr0pa3sh356d9", + "value": 546 + } +*/ +func p2wpkh(scriptpubkey_asm string) []byte { + + pubkeyHash := ExtractHexFromScriptpubkeyAsm(strings.Split(scriptpubkey_asm, " ")) //or the witness program + version := "00" + + pubkeyHashBytes, _ := hex.DecodeString(pubkeyHash) + versionBytes, err := hex.DecodeString(version) + + conv, err := bech32.ConvertBits(pubkeyHashBytes, 8, 5, true) + Handle(err) + + versionPubkeyHash := append(versionBytes, conv...) + address, err := bech32.Encode("bc", versionPubkeyHash) + Handle(err) + return []byte(address) + +} + diff --git a/p2wsh.go b/p2wsh.go new file mode 100644 index 00000000..199fd955 --- /dev/null +++ b/p2wsh.go @@ -0,0 +1,31 @@ +package main + +import ( + "encoding/hex" + "strings" + + "github.com/btcsuite/btcutil/bech32" +) + +/* + { + "scriptpubkey": "002091e3d9fd88a640bf81a6a5997c73340b1b3e6068c9e4e98fe9398621a5d1f561", + "scriptpubkey_asm": "OP_0 OP_PUSHBYTES_32 91e3d9fd88a640bf81a6a5997c73340b1b3e6068c9e4e98fe9398621a5d1f561", + "scriptpubkey_type": "v0_p2wsh", + "scriptpubkey_address": "bc1qj83anlvg5eqtlqdx5kvhcue5pvdnucrge8jwnrlf8xrzrfw374sszral4a", + "value": 326923 + } +*/ +func p2wsh(scriptpubkey_asm string) []byte { + witness_scriptHash := ExtractHexFromScriptpubkeyAsm(strings.Split(scriptpubkey_asm, " ")) + witness_scriptHash_bytes, _ := hex.DecodeString(witness_scriptHash) + version := "00" + version_bytes, _ := hex.DecodeString(version) + + conv, _ := bech32.ConvertBits(witness_scriptHash_bytes, 8, 5, true) + conv = append(version_bytes, conv...) + + hrp := "bc" + encodedAddress, _ := bech32.Encode(hrp, conv) + return []byte(encodedAddress) +} diff --git a/pow.go b/pow.go new file mode 100644 index 00000000..6661e830 --- /dev/null +++ b/pow.go @@ -0,0 +1,64 @@ +package main + +import ( + "encoding/binary" + "encoding/hex" + "time" +) + +const target string = "0000ffff00000000000000000000000000000000000000000000000000000000" + +func SerializeBlockHeader(bh *BlockHeader) []byte { + var serialized []byte + + versionBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(versionBytes, bh.version) + serialized = append(serialized, versionBytes...) + + prevBlockHashbytes, _ := hex.DecodeString(bh.prevBlockHash) + serialized = append(serialized, prevBlockHashbytes...) + + merkleRootbytes, _ := hex.DecodeString(bh.merkleRoot) + serialized = append(serialized, merkleRootbytes...) + + bh.time = time.Now().Unix() + timeBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(timeBytes, uint32(bh.time)) + serialized = append(serialized, timeBytes...) + + bitsBytes, _ := hex.DecodeString(bh.bits) + serialized = append(serialized, bitsBytes...) + + nonceBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(nonceBytes, bh.nonce) + serialized = append(serialized, nonceBytes...) + + return serialized +} +func compareByteArrays(a, b []byte) int { + if len(a) != len(b) { + panic("Arrays must have the same length") + } + + for i := range a { + if a[i] < b[i] { + return -1 + } else if a[i] > b[i] { + return 1 + } + } + + return 0 +} +func ProofOfWork(bh *BlockHeader) bool { + targetBytes, _ := hex.DecodeString(target) + for { + serialized := SerializeBlockHeader(bh) + hash := to_sha(to_sha(serialized)) + + if compareByteArrays(hash, targetBytes) == -1 { + return true + } + bh.nonce++ + } +} diff --git a/run.sh b/run.sh index 721aeb29..c498ab63 100644 --- a/run.sh +++ b/run.sh @@ -1 +1,2 @@ -# Update this file to run your own code \ No newline at end of file +# Update this file to run your own code +go run main.go serialize.go merkle.go witnessMerkle.go pow.go Prioritize.go coinbase.go weight.go \ No newline at end of file diff --git a/serialize.go b/serialize.go new file mode 100644 index 00000000..2b90833c --- /dev/null +++ b/serialize.go @@ -0,0 +1,240 @@ +package main + +import ( + "crypto/sha256" + "encoding/binary" + "encoding/hex" + "fmt" + "os" +) + +type Input struct { + TxID string `json:"txid"` + Vout uint32 `json:"vout"` + Prevout Prevout `json:"prevout"` + Scriptsig string `json:"scriptsig"` + ScriptsigAsm string `json:"scriptsig_asm"` + Witness []string `json:"witness"` + IsCoinbase bool `json:"is_coinbase"` + Sequence uint32 `json:"sequence"` +} + +type Prevout struct { + Scriptpubkey string `json:"scriptpubkey"` + ScriptpubkeyAsm string `json:"scriptpubkey_asm"` + ScriptpubkeyType string `json:"scriptpubkey_type"` + ScriptpubkeyAddress string `json:"scriptpubkey_address"` + Value uint64 `json:"value"` +} + +type Transaction struct { + Version uint32 `json:"version"` + Locktime uint32 `json:"locktime"` + Vin []Input `json:"vin"` + Vout []Prevout `json:"vout"` +} + +var strs []byte + +func CheckSegWit(tx *Transaction) bool { + for _, vin := range tx.Vin { + if len(vin.Witness) > 0 { + return true + } + } + return false +} +func serializeTransaction(tx *Transaction) ([]byte, error) { + + var serialized []byte + // Serialize version + versionBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(versionBytes, tx.Version) + serialized = append(serialized, versionBytes...) + // Serialize vin count + vinCount := uint64(len(tx.Vin)) + serialized = append(serialized, serializeVarInt(vinCount)...) + + // Serialize vin + for _, vin := range tx.Vin { + txidBytes, _ := hex.DecodeString(vin.TxID) + serialized = append(serialized, reverseBytes(txidBytes)...) + + voutBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(voutBytes, vin.Vout) + serialized = append(serialized, voutBytes...) + + Scriptsig_bytes, _ := hex.DecodeString(vin.Scriptsig) + length_scriptsig := (uint64(len(Scriptsig_bytes))) + serialized = append(serialized, serializeVarInt(length_scriptsig)...) + + serialized = append(serialized, Scriptsig_bytes...) + + // Serialize sequence + sequenceBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(sequenceBytes, vin.Sequence) + serialized = append(serialized, sequenceBytes...) + + } + + // Serialize vout count + voutCount := uint64(len(tx.Vout)) + serialized = append(serialized, serializeVarInt(voutCount)...) + + // Serialize vout + for _, vout := range tx.Vout { + valueBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(valueBytes, vout.Value) + serialized = append(serialized, valueBytes...) + + // Serialize scriptPubKey length + scriptPubKeyBytes, err := hex.DecodeString(vout.Scriptpubkey) + scriptPubKeyLen := uint64(len(scriptPubKeyBytes)) // Divide by 2 if appending the length of the non decoded form to get byte length since scriptPubKey is hex encoded + serialized = append(serialized, serializeVarInt(scriptPubKeyLen)...) + + // Serialize scriptPubKey + if err != nil { + return nil, err + } + serialized = append(serialized, scriptPubKeyBytes...) + } + //Locktime + locktimeBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(locktimeBytes, tx.Locktime) + serialized = append(serialized, locktimeBytes...) + + return serialized, nil +} +func SegWitSerialize(tx *Transaction) ([]byte, error) { + + var serialized []byte + isSegwit := CheckSegWit(tx) + // Serialize version + versionBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(versionBytes, tx.Version) + serialized = append(serialized, versionBytes...) + // Serialize vin count + if isSegwit { + serialized = append(serialized, []byte{0x00, 0x01}...) + } + vinCount := uint64(len(tx.Vin)) + serialized = append(serialized, serializeVarInt(vinCount)...) + + // Serialize vin + for _, vin := range tx.Vin { + txidBytes, _ := hex.DecodeString(vin.TxID) + serialized = append(serialized, reverseBytes(txidBytes)...) + + voutBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(voutBytes, vin.Vout) + serialized = append(serialized, voutBytes...) + + Scriptsig_bytes, _ := hex.DecodeString(vin.Scriptsig) + length_scriptsig := (uint64(len(Scriptsig_bytes))) + serialized = append(serialized, serializeVarInt(length_scriptsig)...) + + serialized = append(serialized, Scriptsig_bytes...) + + // Serialize sequence + sequenceBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(sequenceBytes, vin.Sequence) + serialized = append(serialized, sequenceBytes...) + + } + + // Serialize vout count + voutCount := uint64(len(tx.Vout)) + serialized = append(serialized, serializeVarInt(voutCount)...) + + // Serialize vout + for _, vout := range tx.Vout { + valueBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(valueBytes, vout.Value) + serialized = append(serialized, valueBytes...) + + // Serialize scriptPubKey length + scriptPubKeyBytes, err := hex.DecodeString(vout.Scriptpubkey) + scriptPubKeyLen := uint64(len(scriptPubKeyBytes)) // Divide by 2 if appending the length of the non decoded form to get byte length since scriptPubKey is hex encoded + serialized = append(serialized, serializeVarInt(scriptPubKeyLen)...) + + // Serialize scriptPubKey + if err != nil { + return nil, err + } + serialized = append(serialized, scriptPubKeyBytes...) + } + //Locktime + if isSegwit { + for _, vin := range tx.Vin { + witnessCount := uint64(len(vin.Witness)) + serialized = append(serialized, serializeVarInt(witnessCount)...) + for _, witness := range vin.Witness { + witnessBytes, _ := hex.DecodeString(witness) + witnessLen := uint64(len(witnessBytes)) + serialized = append(serialized, serializeVarInt(witnessLen)...) + serialized = append(serialized, witnessBytes...) + } + } + } + locktimeBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(locktimeBytes, tx.Locktime) + serialized = append(serialized, locktimeBytes...) + return serialized, nil +} + +func reverseBytes(data []byte) []byte { + length := len(data) + for i := 0; i < length/2; i++ { + data[i], data[length-i-1] = data[length-i-1], data[i] + } + return data +} + +func serializeVarInt(n uint64) []byte { + if n < 0xfd { + return []byte{byte(n)} + } else if n <= 0xffff { + return append([]byte{0xfd}, uint16ToBytes(uint16(n))...) + } else if n <= 0xffffffff { + return append([]byte{0xfe}, uint32ToBytes(uint32(n))...) + } else { + return append([]byte{0xff}, uint64ToBytes(n)...) + } +} + +func uint16ToBytes(n uint16) []byte { + bytes := make([]byte, 2) + binary.LittleEndian.PutUint16(bytes, n) + return bytes +} + +func uint32ToBytes(n uint32) []byte { + bytes := make([]byte, 4) + binary.LittleEndian.PutUint32(bytes, n) + return bytes +} + +func uint64ToBytes(n uint64) []byte { + bytes := make([]byte, 8) + binary.LittleEndian.PutUint64(bytes, n) + return bytes +} + +func to_sha(data []byte) []byte { + hash := sha256.Sum256(data) + return hash[:] +} + +func jsonData(filename string) (string, error) { + data, err := os.ReadFile(filename) + if err != nil { + return "", err + } + return string(data), nil +} + +func Handle(err error) { + if err != nil { + fmt.Println(err) + } +} diff --git a/signature.go b/signature.go new file mode 100644 index 00000000..8c5ed4dd --- /dev/null +++ b/signature.go @@ -0,0 +1,32 @@ +package main + +import ( + "encoding/binary" + "encoding/json" + "fmt" +) + +func Signature() { + txData, err := jsonData("./mempool/0ac528562a1626863c0cb912eb725530c54e786e6485380c16633e4b9bce1720.json") + Handle(err) + var tx Transaction + err = json.Unmarshal([]byte(txData), &tx) + for idx := range tx.Vin { + // fmt.Println(vin.Scriptsig) + tx.Vin[idx].Scriptsig = tx.Vin[idx].Prevout.Scriptpubkey + // fmt.Println(vin.Scriptsig) + } + serialized, _ := serializeTransaction(&tx) + fmt.Printf("%x\n", serialized) + scriptsigHash_type := make([]byte, 4) + binary.LittleEndian.PutUint32(scriptsigHash_type, 1) + serialized = append(serialized, scriptsigHash_type...) + fmt.Printf("%x\n", serialized) + + hash256_serialized := to_sha(to_sha(serialized)) + fmt.Printf("%x\n", hash256_serialized) + //Now we need to sign the transaction hash: Now we can just sign this message hash just like we would sign any message in ECDSA. All we need is our private key, and a randomly generated nonce. + //The private key is the one that was used to create the public key that the output has been locked to. + //derive the r and s values of the signature: + +} diff --git a/test.json b/test.json new file mode 100644 index 00000000..e69de29b diff --git a/vendor/github.com/btcsuite/btcutil/LICENSE b/vendor/github.com/btcsuite/btcutil/LICENSE new file mode 100644 index 00000000..3e7b1679 --- /dev/null +++ b/vendor/github.com/btcsuite/btcutil/LICENSE @@ -0,0 +1,16 @@ +ISC License + +Copyright (c) 2013-2017 The btcsuite developers +Copyright (c) 2016-2017 The Lightning Network Developers + +Permission to use, copy, modify, and distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/vendor/github.com/btcsuite/btcutil/bech32/README.md b/vendor/github.com/btcsuite/btcutil/bech32/README.md new file mode 100644 index 00000000..e6fa43f5 --- /dev/null +++ b/vendor/github.com/btcsuite/btcutil/bech32/README.md @@ -0,0 +1,29 @@ +bech32 +========== + +[![Build Status](http://img.shields.io/travis/btcsuite/btcutil.svg)](https://travis-ci.org/btcsuite/btcutil) +[![ISC License](http://img.shields.io/badge/license-ISC-blue.svg)](http://copyfree.org) +[![GoDoc](https://godoc.org/github.com/btcsuite/btcutil/bech32?status.png)](http://godoc.org/github.com/btcsuite/btcutil/bech32) + +Package bech32 provides a Go implementation of the bech32 format specified in +[BIP 173](https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki). + +Test vectors from BIP 173 are added to ensure compatibility with the BIP. + +## Installation and Updating + +```bash +$ go get -u github.com/btcsuite/btcutil/bech32 +``` + +## Examples + +* [Bech32 decode Example](http://godoc.org/github.com/btcsuite/btcutil/bech32#example-Bech32Decode) + Demonstrates how to decode a bech32 encoded string. +* [Bech32 encode Example](http://godoc.org/github.com/btcsuite/btcutil/bech32#example-BechEncode) + Demonstrates how to encode data into a bech32 string. + +## License + +Package bech32 is licensed under the [copyfree](http://copyfree.org) ISC +License. diff --git a/vendor/github.com/btcsuite/btcutil/bech32/bech32.go b/vendor/github.com/btcsuite/btcutil/bech32/bech32.go new file mode 100644 index 00000000..8bda0a1d --- /dev/null +++ b/vendor/github.com/btcsuite/btcutil/bech32/bech32.go @@ -0,0 +1,252 @@ +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +package bech32 + +import ( + "fmt" + "strings" +) + +const charset = "qpzry9x8gf2tvdw0s3jn54khce6mua7l" + +var gen = []int{0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd, 0x2a1462b3} + +// Decode decodes a bech32 encoded string, returning the human-readable +// part and the data part excluding the checksum. +func Decode(bech string) (string, []byte, error) { + // The maximum allowed length for a bech32 string is 90. It must also + // be at least 8 characters, since it needs a non-empty HRP, a + // separator, and a 6 character checksum. + if len(bech) < 8 || len(bech) > 90 { + return "", nil, fmt.Errorf("invalid bech32 string length %d", + len(bech)) + } + // Only ASCII characters between 33 and 126 are allowed. + for i := 0; i < len(bech); i++ { + if bech[i] < 33 || bech[i] > 126 { + return "", nil, fmt.Errorf("invalid character in "+ + "string: '%c'", bech[i]) + } + } + + // The characters must be either all lowercase or all uppercase. + lower := strings.ToLower(bech) + upper := strings.ToUpper(bech) + if bech != lower && bech != upper { + return "", nil, fmt.Errorf("string not all lowercase or all " + + "uppercase") + } + + // We'll work with the lowercase string from now on. + bech = lower + + // The string is invalid if the last '1' is non-existent, it is the + // first character of the string (no human-readable part) or one of the + // last 6 characters of the string (since checksum cannot contain '1'), + // or if the string is more than 90 characters in total. + one := strings.LastIndexByte(bech, '1') + if one < 1 || one+7 > len(bech) { + return "", nil, fmt.Errorf("invalid index of 1") + } + + // The human-readable part is everything before the last '1'. + hrp := bech[:one] + data := bech[one+1:] + + // Each character corresponds to the byte with value of the index in + // 'charset'. + decoded, err := toBytes(data) + if err != nil { + return "", nil, fmt.Errorf("failed converting data to bytes: "+ + "%v", err) + } + + if !bech32VerifyChecksum(hrp, decoded) { + moreInfo := "" + checksum := bech[len(bech)-6:] + expected, err := toChars(bech32Checksum(hrp, + decoded[:len(decoded)-6])) + if err == nil { + moreInfo = fmt.Sprintf("Expected %v, got %v.", + expected, checksum) + } + return "", nil, fmt.Errorf("checksum failed. " + moreInfo) + } + + // We exclude the last 6 bytes, which is the checksum. + return hrp, decoded[:len(decoded)-6], nil +} + +// Encode encodes a byte slice into a bech32 string with the +// human-readable part hrb. Note that the bytes must each encode 5 bits +// (base32). +func Encode(hrp string, data []byte) (string, error) { + // Calculate the checksum of the data and append it at the end. + checksum := bech32Checksum(hrp, data) + combined := append(data, checksum...) + + // The resulting bech32 string is the concatenation of the hrp, the + // separator 1, data and checksum. Everything after the separator is + // represented using the specified charset. + dataChars, err := toChars(combined) + if err != nil { + return "", fmt.Errorf("unable to convert data bytes to chars: "+ + "%v", err) + } + return hrp + "1" + dataChars, nil +} + +// toBytes converts each character in the string 'chars' to the value of the +// index of the correspoding character in 'charset'. +func toBytes(chars string) ([]byte, error) { + decoded := make([]byte, 0, len(chars)) + for i := 0; i < len(chars); i++ { + index := strings.IndexByte(charset, chars[i]) + if index < 0 { + return nil, fmt.Errorf("invalid character not part of "+ + "charset: %v", chars[i]) + } + decoded = append(decoded, byte(index)) + } + return decoded, nil +} + +// toChars converts the byte slice 'data' to a string where each byte in 'data' +// encodes the index of a character in 'charset'. +func toChars(data []byte) (string, error) { + result := make([]byte, 0, len(data)) + for _, b := range data { + if int(b) >= len(charset) { + return "", fmt.Errorf("invalid data byte: %v", b) + } + result = append(result, charset[b]) + } + return string(result), nil +} + +// ConvertBits converts a byte slice where each byte is encoding fromBits bits, +// to a byte slice where each byte is encoding toBits bits. +func ConvertBits(data []byte, fromBits, toBits uint8, pad bool) ([]byte, error) { + if fromBits < 1 || fromBits > 8 || toBits < 1 || toBits > 8 { + return nil, fmt.Errorf("only bit groups between 1 and 8 allowed") + } + + // The final bytes, each byte encoding toBits bits. + var regrouped []byte + + // Keep track of the next byte we create and how many bits we have + // added to it out of the toBits goal. + nextByte := byte(0) + filledBits := uint8(0) + + for _, b := range data { + + // Discard unused bits. + b = b << (8 - fromBits) + + // How many bits remaining to extract from the input data. + remFromBits := fromBits + for remFromBits > 0 { + // How many bits remaining to be added to the next byte. + remToBits := toBits - filledBits + + // The number of bytes to next extract is the minimum of + // remFromBits and remToBits. + toExtract := remFromBits + if remToBits < toExtract { + toExtract = remToBits + } + + // Add the next bits to nextByte, shifting the already + // added bits to the left. + nextByte = (nextByte << toExtract) | (b >> (8 - toExtract)) + + // Discard the bits we just extracted and get ready for + // next iteration. + b = b << toExtract + remFromBits -= toExtract + filledBits += toExtract + + // If the nextByte is completely filled, we add it to + // our regrouped bytes and start on the next byte. + if filledBits == toBits { + regrouped = append(regrouped, nextByte) + filledBits = 0 + nextByte = 0 + } + } + } + + // We pad any unfinished group if specified. + if pad && filledBits > 0 { + nextByte = nextByte << (toBits - filledBits) + regrouped = append(regrouped, nextByte) + filledBits = 0 + nextByte = 0 + } + + // Any incomplete group must be <= 4 bits, and all zeroes. + if filledBits > 0 && (filledBits > 4 || nextByte != 0) { + return nil, fmt.Errorf("invalid incomplete group") + } + + return regrouped, nil +} + +// For more details on the checksum calculation, please refer to BIP 173. +func bech32Checksum(hrp string, data []byte) []byte { + // Convert the bytes to list of integers, as this is needed for the + // checksum calculation. + integers := make([]int, len(data)) + for i, b := range data { + integers[i] = int(b) + } + values := append(bech32HrpExpand(hrp), integers...) + values = append(values, []int{0, 0, 0, 0, 0, 0}...) + polymod := bech32Polymod(values) ^ 1 + var res []byte + for i := 0; i < 6; i++ { + res = append(res, byte((polymod>>uint(5*(5-i)))&31)) + } + return res +} + +// For more details on the polymod calculation, please refer to BIP 173. +func bech32Polymod(values []int) int { + chk := 1 + for _, v := range values { + b := chk >> 25 + chk = (chk&0x1ffffff)<<5 ^ v + for i := 0; i < 5; i++ { + if (b>>uint(i))&1 == 1 { + chk ^= gen[i] + } + } + } + return chk +} + +// For more details on HRP expansion, please refer to BIP 173. +func bech32HrpExpand(hrp string) []int { + v := make([]int, 0, len(hrp)*2+1) + for i := 0; i < len(hrp); i++ { + v = append(v, int(hrp[i]>>5)) + } + v = append(v, 0) + for i := 0; i < len(hrp); i++ { + v = append(v, int(hrp[i]&31)) + } + return v +} + +// For more details on the checksum verification, please refer to BIP 173. +func bech32VerifyChecksum(hrp string, data []byte) bool { + integers := make([]int, len(data)) + for i, b := range data { + integers[i] = int(b) + } + concat := append(bech32HrpExpand(hrp), integers...) + return bech32Polymod(concat) == 1 +} diff --git a/vendor/github.com/btcsuite/btcutil/bech32/doc.go b/vendor/github.com/btcsuite/btcutil/bech32/doc.go new file mode 100644 index 00000000..2d64fbe0 --- /dev/null +++ b/vendor/github.com/btcsuite/btcutil/bech32/doc.go @@ -0,0 +1,15 @@ +// Copyright (c) 2017 The btcsuite developers +// Use of this source code is governed by an ISC +// license that can be found in the LICENSE file. + +/* +Package bech32 provides a Go implementation of the bech32 format specified in +BIP 173. + +Bech32 strings consist of a human-readable part (hrp), followed by the +separator 1, then a checksummed data part encoded using the 32 characters +"qpzry9x8gf2tvdw0s3jn54khce6mua7l". + +More info: https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki +*/ +package bech32 diff --git a/vendor/github.com/mr-tron/base58/.gitignore b/vendor/github.com/mr-tron/base58/.gitignore new file mode 100644 index 00000000..9f11b755 --- /dev/null +++ b/vendor/github.com/mr-tron/base58/.gitignore @@ -0,0 +1 @@ +.idea/ diff --git a/vendor/github.com/mr-tron/base58/LICENSE b/vendor/github.com/mr-tron/base58/LICENSE new file mode 100644 index 00000000..cb7829a2 --- /dev/null +++ b/vendor/github.com/mr-tron/base58/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2017 Denis Subbotin +Copyright (c) 2017 Nika Jones +Copyright (c) 2017 Philip Schlump + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/mr-tron/base58/README.md b/vendor/github.com/mr-tron/base58/README.md new file mode 100644 index 00000000..b37d61cb --- /dev/null +++ b/vendor/github.com/mr-tron/base58/README.md @@ -0,0 +1,48 @@ +# Fast Implementation of Base58 encoding +[![GoDoc](https://godoc.org/github.com/mr-tron/base58?status.svg)](https://godoc.org/github.com/mr-tron/base58) [![Go Report Card](https://goreportcard.com/badge/github.com/mr-tron/base58)](https://goreportcard.com/report/github.com/mr-tron/base58) +[![Used By](https://sourcegraph.com/github.com/mr-tron/base58/-/badge.svg)](https://sourcegraph.com/github.com/mr-tron/base58?badge) + +Fast implementation of base58 encoding in Go. + +Base algorithm is copied from https://github.com/trezor/trezor-crypto/blob/master/base58.c + +## Benchmark +Trivial - encoding via big.Int (over libraries use this implemenation) +Fast - optimized algorythm from trezor + +``` +BenchmarkTrivialBase58Encoding-4 123063 9568 ns/op +BenchmarkFastBase58Encoding-4 690040 1598 ns/op + +BenchmarkTrivialBase58Decoding-4 275216 4301 ns/op +BenchmarkFastBase58Decoding-4 1812105 658 ns/op +``` +Encoding - **faster by 6 times** + +Decoding - **faster by 6 times** + +## Usage example + +```go + +package main + +import ( + "fmt" + "github.com/mr-tron/base58" +) + +func main() { + + encoded := "1QCaxc8hutpdZ62iKZsn1TCG3nh7uPZojq" + num, err := base58.Decode(encoded) + if err != nil { + fmt.Printf("Demo %v, got error %s\n", encoded, err) + } + chk := base58.Encode(num) + if encoded == string(chk) { + fmt.Printf ( "Successfully decoded then re-encoded %s\n", encoded ) + } +} + +``` diff --git a/vendor/github.com/mr-tron/base58/alphabet.go b/vendor/github.com/mr-tron/base58/alphabet.go new file mode 100644 index 00000000..a0f88783 --- /dev/null +++ b/vendor/github.com/mr-tron/base58/alphabet.go @@ -0,0 +1,31 @@ +package base58 + +// Alphabet is a a b58 alphabet. +type Alphabet struct { + decode [128]int8 + encode [58]byte +} + +// NewAlphabet creates a new alphabet from the passed string. +// +// It panics if the passed string is not 58 bytes long or isn't valid ASCII. +func NewAlphabet(s string) *Alphabet { + if len(s) != 58 { + panic("base58 alphabets must be 58 bytes long") + } + ret := new(Alphabet) + copy(ret.encode[:], s) + for i := range ret.decode { + ret.decode[i] = -1 + } + for i, b := range ret.encode { + ret.decode[b] = int8(i) + } + return ret +} + +// BTCAlphabet is the bitcoin base58 alphabet. +var BTCAlphabet = NewAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + +// FlickrAlphabet is the flickr base58 alphabet. +var FlickrAlphabet = NewAlphabet("123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ") diff --git a/vendor/github.com/mr-tron/base58/base58.go b/vendor/github.com/mr-tron/base58/base58.go new file mode 100644 index 00000000..b1d592ca --- /dev/null +++ b/vendor/github.com/mr-tron/base58/base58.go @@ -0,0 +1,150 @@ +package base58 + +import ( + "fmt" +) + +// Encode encodes the passed bytes into a base58 encoded string. +func Encode(bin []byte) string { + return FastBase58EncodingAlphabet(bin, BTCAlphabet) +} + +// EncodeAlphabet encodes the passed bytes into a base58 encoded string with the +// passed alphabet. +func EncodeAlphabet(bin []byte, alphabet *Alphabet) string { + return FastBase58EncodingAlphabet(bin, alphabet) +} + +// FastBase58Encoding encodes the passed bytes into a base58 encoded string. +func FastBase58Encoding(bin []byte) string { + return FastBase58EncodingAlphabet(bin, BTCAlphabet) +} + +// FastBase58EncodingAlphabet encodes the passed bytes into a base58 encoded +// string with the passed alphabet. +func FastBase58EncodingAlphabet(bin []byte, alphabet *Alphabet) string { + + size := len(bin) + + zcount := 0 + for zcount < size && bin[zcount] == 0 { + zcount++ + } + + // It is crucial to make this as short as possible, especially for + // the usual case of bitcoin addrs + size = zcount + + // This is an integer simplification of + // ceil(log(256)/log(58)) + (size-zcount)*555/406 + 1 + + out := make([]byte, size) + + var i, high int + var carry uint32 + + high = size - 1 + for _, b := range bin { + i = size - 1 + for carry = uint32(b); i > high || carry != 0; i-- { + carry = carry + 256*uint32(out[i]) + out[i] = byte(carry % 58) + carry /= 58 + } + high = i + } + + // Determine the additional "zero-gap" in the buffer (aside from zcount) + for i = zcount; i < size && out[i] == 0; i++ { + } + + // Now encode the values with actual alphabet in-place + val := out[i-zcount:] + size = len(val) + for i = 0; i < size; i++ { + out[i] = alphabet.encode[val[i]] + } + + return string(out[:size]) +} + +// Decode decodes the base58 encoded bytes. +func Decode(str string) ([]byte, error) { + return FastBase58DecodingAlphabet(str, BTCAlphabet) +} + +// DecodeAlphabet decodes the base58 encoded bytes using the given b58 alphabet. +func DecodeAlphabet(str string, alphabet *Alphabet) ([]byte, error) { + return FastBase58DecodingAlphabet(str, alphabet) +} + +// FastBase58Decoding decodes the base58 encoded bytes. +func FastBase58Decoding(str string) ([]byte, error) { + return FastBase58DecodingAlphabet(str, BTCAlphabet) +} + +// FastBase58DecodingAlphabet decodes the base58 encoded bytes using the given +// b58 alphabet. +func FastBase58DecodingAlphabet(str string, alphabet *Alphabet) ([]byte, error) { + if len(str) == 0 { + return nil, fmt.Errorf("zero length string") + } + + zero := alphabet.encode[0] + b58sz := len(str) + + var zcount int + for i := 0; i < b58sz && str[i] == zero; i++ { + zcount++ + } + + var t, c uint64 + + // the 32bit algo stretches the result up to 2 times + binu := make([]byte, 2*((b58sz*406/555)+1)) + outi := make([]uint32, (b58sz+3)/4) + + for _, r := range str { + if r > 127 { + return nil, fmt.Errorf("high-bit set on invalid digit") + } + if alphabet.decode[r] == -1 { + return nil, fmt.Errorf("invalid base58 digit (%q)", r) + } + + c = uint64(alphabet.decode[r]) + + for j := len(outi) - 1; j >= 0; j-- { + t = uint64(outi[j])*58 + c + c = t >> 32 + outi[j] = uint32(t & 0xffffffff) + } + } + + // initial mask depends on b58sz, on further loops it always starts at 24 bits + mask := (uint(b58sz%4) * 8) + if mask == 0 { + mask = 32 + } + mask -= 8 + + outLen := 0 + for j := 0; j < len(outi); j++ { + for mask < 32 { // loop relies on uint overflow + binu[outLen] = byte(outi[j] >> mask) + mask -= 8 + outLen++ + } + mask = 24 + } + + // find the most significant byte post-decode, if any + for msb := zcount; msb < len(binu); msb++ { + if binu[msb] > 0 { + return binu[msb-zcount : outLen], nil + } + } + + // it's all zeroes + return binu[:outLen], nil +} diff --git a/vendor/github.com/mr-tron/base58/doc.go b/vendor/github.com/mr-tron/base58/doc.go new file mode 100644 index 00000000..e1bdc0d7 --- /dev/null +++ b/vendor/github.com/mr-tron/base58/doc.go @@ -0,0 +1,21 @@ +/* +Package base58 provides fast implementation of base58 encoding. + +Base58 Usage + +To decode a base58 string: + + encoded := "1QCaxc8hutpdZ62iKZsn1TCG3nh7uPZojq" + buf, _ := base58.Decode(encoded) + +To encode the same data: + + encoded := base58.Encode(buf) + +With custom alphabet + + customAlphabet := base58.NewAlphabet("123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + encoded := base58.EncodeAlphabet(buf, customAlphabet) + +*/ +package base58 diff --git a/vendor/github.com/mr-tron/base58/trivial.go b/vendor/github.com/mr-tron/base58/trivial.go new file mode 100644 index 00000000..eff54f41 --- /dev/null +++ b/vendor/github.com/mr-tron/base58/trivial.go @@ -0,0 +1,74 @@ +package base58 + +import ( + "fmt" + "math/big" +) + +var ( + bn0 = big.NewInt(0) + bn58 = big.NewInt(58) +) + +// TrivialBase58Encoding encodes the passed bytes into a base58 encoded string +// (inefficiently). +func TrivialBase58Encoding(a []byte) string { + return TrivialBase58EncodingAlphabet(a, BTCAlphabet) +} + +// TrivialBase58EncodingAlphabet encodes the passed bytes into a base58 encoded +// string (inefficiently) with the passed alphabet. +func TrivialBase58EncodingAlphabet(a []byte, alphabet *Alphabet) string { + zero := alphabet.encode[0] + idx := len(a)*138/100 + 1 + buf := make([]byte, idx) + bn := new(big.Int).SetBytes(a) + var mo *big.Int + for bn.Cmp(bn0) != 0 { + bn, mo = bn.DivMod(bn, bn58, new(big.Int)) + idx-- + buf[idx] = alphabet.encode[mo.Int64()] + } + for i := range a { + if a[i] != 0 { + break + } + idx-- + buf[idx] = zero + } + return string(buf[idx:]) +} + +// TrivialBase58Decoding decodes the base58 encoded bytes (inefficiently). +func TrivialBase58Decoding(str string) ([]byte, error) { + return TrivialBase58DecodingAlphabet(str, BTCAlphabet) +} + +// TrivialBase58DecodingAlphabet decodes the base58 encoded bytes +// (inefficiently) using the given b58 alphabet. +func TrivialBase58DecodingAlphabet(str string, alphabet *Alphabet) ([]byte, error) { + zero := alphabet.encode[0] + + var zcount int + for i := 0; i < len(str) && str[i] == zero; i++ { + zcount++ + } + leading := make([]byte, zcount) + + var padChar rune = -1 + src := []byte(str) + j := 0 + for ; j < len(src) && src[j] == byte(padChar); j++ { + } + + n := new(big.Int) + for i := range src[j:] { + c := alphabet.decode[src[i]] + if c == -1 { + return nil, fmt.Errorf("illegal base58 data at input index: %d", i) + } + n.Mul(n, bn58) + n.Add(n, big.NewInt(int64(c))) + } + return append(leading, n.Bytes()...), nil +} diff --git a/vendor/modules.txt b/vendor/modules.txt new file mode 100644 index 00000000..fde0a197 --- /dev/null +++ b/vendor/modules.txt @@ -0,0 +1,6 @@ +# github.com/btcsuite/btcutil v1.0.2 +## explicit; go 1.13 +github.com/btcsuite/btcutil/bech32 +# github.com/mr-tron/base58 v1.2.0 +## explicit; go 1.12 +github.com/mr-tron/base58 diff --git a/weight.go b/weight.go new file mode 100644 index 00000000..f6445102 --- /dev/null +++ b/weight.go @@ -0,0 +1,50 @@ +package main + +import ( + "encoding/hex" +) + +const ( + WUPerByteNonWitness = 4 + WUPerByteWitness = 1 +) + +type TxWeight struct { + BaseSize int `json:"base_size"` // Size of non-witness data in bytes + WitnessSize int `json:"witness_size"` // Size of witness data in bytes + Weight int `json:"weight"` // Total weight in weight units +} + +// Function to calculate transaction weight +// Function to calculate base size +func CalculateBaseSize(tx *Transaction) int { + serialised, _ := serializeTransaction(tx) + return len(serialised) +} + +// Function to calculate witness size +func calculateWitnessSize(tx *Transaction) int { + if !CheckSegWit(tx) { + return 0 + + } + // Inputs (witness) + var serialized []byte + isSegwit := CheckSegWit(tx) + if isSegwit { + serialized = append(serialized, []byte{0x00, 0x01}...) + } + if isSegwit { + for _, vin := range tx.Vin { + witnessCount := uint64(len(vin.Witness)) + serialized = append(serialized, serializeVarInt(witnessCount)...) + for _, witness := range vin.Witness { + witnessBytes, _ := hex.DecodeString(witness) + witnessLen := uint64(len(witnessBytes)) + serialized = append(serialized, serializeVarInt(witnessLen)...) + serialized = append(serialized, witnessBytes...) + } + } + } + return len(serialized) +} \ No newline at end of file diff --git a/witnessMerkle.go b/witnessMerkle.go new file mode 100644 index 00000000..17f63650 --- /dev/null +++ b/witnessMerkle.go @@ -0,0 +1,16 @@ +package main + +import ( + "encoding/hex" +) + +func CreateWitnessMerkle() string { + _, _, wTxIDs := Prioritize() + wTxIDs = append([]string{"0000000000000000000000000000000000000000000000000000000000000000"}, wTxIDs...) + merkleRoot := NewMerkleTree(wTxIDs) + // fmt.Println("Witness Merkle Root: ", hex.EncodeToString(merkleRoot.Data)) + commitment_string := hex.EncodeToString(merkleRoot.Data) + "0000000000000000000000000000000000000000000000000000000000000000" + WitnessCommitment, _ := hex.DecodeString(commitment_string) + WitnessCommitment = to_sha(to_sha(WitnessCommitment)) + return hex.EncodeToString(WitnessCommitment) +}