diff --git a/main.go b/main.go index 21bfe52..91516b4 100644 --- a/main.go +++ b/main.go @@ -348,6 +348,37 @@ func Ordering() (uint64, []string, []string) { // reward += tx.Fee return reward, allowedTxIDs, notAllowedTxIDs } +func calBaseSize(tx *Transaction) int { + serialised, _ := serTx(tx) + return len(serialised) +} + +// Function to calculate witness size +func calWitSize(tx *Transaction) int { + if !isSegWitTransaction(tx) { + return 0 + + } + // Inputs (witness) + var serlzd []byte + isSegwit := isSegWitTransaction(tx) + if isSegwit { + serlzd = append(serlzd, []byte{0x00, 0x01}...) + } + if isSegwit { + for _, vin := range tx.Vin { + witnessCount := uint64(len(vin.Witness)) + serlzd = append(serlzd, SerializeVarInt(witnessCount)...) + for _, witness := range vin.Witness { + witnessBytes, _ := hex.DecodeString(witness) + witnessLen := uint64(len(witnessBytes)) + serlzd = append(serlzd, SerializeVarInt(witnessLen)...) + serlzd = append(serlzd, witnessBytes...) + } + } + } + return len(serlzd) +} func SerializeVarInt(n uint64) []byte { if n < 0xfd { @@ -363,70 +394,13 @@ func SerializeVarInt(n uint64) []byte { func serTx(tx *Transaction) ([]byte, error) { - var serialized []byte - - // Serialize version - serialized = append(serialized, uint32ToLittleEndian(tx.Version)...) - - // Serialize vin count - serialized = append(serialized, SerializeVarInt(uint64(len(tx.Vin)))...) - - // Serialize vin - for _, vin := range tx.Vin { - txidBytes, _ := hex.DecodeString(vin.TxID) - serialized = append(serialized, reverseBytes(txidBytes)...) - serialized = append(serialized, uint32ToLittleEndian(vin.Vout)...) - - scriptSigBytes, _ := hex.DecodeString(vin.Scriptsig) - serialized = append(serialized, SerializeVarInt(uint64(len(scriptSigBytes)))...) - serialized = append(serialized, scriptSigBytes...) - - serialized = append(serialized, uint32ToLittleEndian(vin.Sequence)...) - } - - // Serialize vout count - serialized = append(serialized, SerializeVarInt(uint64(len(tx.Vout)))...) - - // Serialize vout - for _, vout := range tx.Vout { - serialized = append(serialized, uint64ToLittleEndian(vout.Value)...) - - scriptPubKeyBytes, err := hex.DecodeString(vout.Scriptpubkey) - if err != nil { - return nil, err - } - serialized = append(serialized, SerializeVarInt(uint64(len(scriptPubKeyBytes)))...) - serialized = append(serialized, scriptPubKeyBytes...) - } - - // Serialize locktime - serialized = append(serialized, uint32ToLittleEndian(tx.Locktime)...) - - return serialized, nil -} -func uint32ToLittleEndian(value uint32) []byte { - bytes := make([]byte, 4) - binary.LittleEndian.PutUint32(bytes, value) - return bytes -} - -func uint64ToLittleEndian(value uint64) []byte { - bytes := make([]byte, 8) - binary.LittleEndian.PutUint64(bytes, value) - return bytes -} -func SegWitSerialize(tx *Transaction) ([]byte, error) { - var serlzd []byte - isSegwit := isSegWitTransaction(tx) // Serialize version + versionBytes := make([]byte, 4) binary.LittleEndian.PutUint32(versionBytes, tx.Version) serlzd = append(serlzd, versionBytes...) // Serialize vin count - if isSegwit { - serlzd = append(serlzd, []byte{0x00, 0x01}...) - } vinCount := uint64(len(tx.Vin)) serlzd = append(serlzd, SerializeVarInt(vinCount)...) @@ -473,25 +447,13 @@ func SegWitSerialize(tx *Transaction) ([]byte, error) { } serlzd = append(serlzd, scriptPubKeyBytes...) } - //For Locktime - if isSegwit { - for _, vin := range tx.Vin { - witnessCount := uint64(len(vin.Witness)) - serlzd = append(serlzd, SerializeVarInt(witnessCount)...) - for _, witness := range vin.Witness { - witnessBytes, _ := hex.DecodeString(witness) - witnessLen := uint64(len(witnessBytes)) - serlzd = append(serlzd, SerializeVarInt(witnessLen)...) - serlzd = append(serlzd, witnessBytes...) - } - } - } + //Locktime locktimeBytes := make([]byte, 4) binary.LittleEndian.PutUint32(locktimeBytes, tx.Locktime) serlzd = append(serlzd, locktimeBytes...) + return serlzd, nil } - func srlzBhead(bh *BlockHeader) []byte { var serlzd []byte @@ -520,26 +482,65 @@ func srlzBhead(bh *BlockHeader) []byte { return serlzd } +func SegWitSerialize(tx *Transaction) ([]byte, error) { -// Function to calculate transaction weight -// Function to calculate base size -func calBaseSize(tx *Transaction) int { - serialised, _ := serTx(tx) - return len(serialised) -} - -// Function to calculate witness size -func calWitSize(tx *Transaction) int { - if !isSegWitTransaction(tx) { - return 0 - - } - // Inputs (witness) var serlzd []byte isSegwit := isSegWitTransaction(tx) + // Serialize version + versionBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(versionBytes, tx.Version) + serlzd = append(serlzd, versionBytes...) + // Serialize vin count if isSegwit { serlzd = append(serlzd, []byte{0x00, 0x01}...) } + vinCount := uint64(len(tx.Vin)) + serlzd = append(serlzd, SerializeVarInt(vinCount)...) + + // Serialize vin + for _, vin := range tx.Vin { + txidBytes, _ := hex.DecodeString(vin.TxID) + serlzd = append(serlzd, reverseBytes(txidBytes)...) + + voutBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(voutBytes, vin.Vout) + serlzd = append(serlzd, voutBytes...) + + Scriptsig_bytes, _ := hex.DecodeString(vin.Scriptsig) + length_scriptsig := (uint64(len(Scriptsig_bytes))) + serlzd = append(serlzd, SerializeVarInt(length_scriptsig)...) + + serlzd = append(serlzd, Scriptsig_bytes...) + + // Serialize sequence + sequenceBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(sequenceBytes, vin.Sequence) + serlzd = append(serlzd, sequenceBytes...) + + } + + // Serialize vout count + voutCount := uint64(len(tx.Vout)) + serlzd = append(serlzd, SerializeVarInt(voutCount)...) + + // Serialize vout + for _, vout := range tx.Vout { + valueBytes := make([]byte, 8) + binary.LittleEndian.PutUint64(valueBytes, vout.Value) + serlzd = append(serlzd, 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 + serlzd = append(serlzd, SerializeVarInt(scriptPubKeyLen)...) + + // Serialize scriptPubKey + if err != nil { + return nil, err + } + serlzd = append(serlzd, scriptPubKeyBytes...) + } + //For Locktime if isSegwit { for _, vin := range tx.Vin { witnessCount := uint64(len(vin.Witness)) @@ -552,7 +553,10 @@ func calWitSize(tx *Transaction) int { } } } - return len(serlzd) + locktimeBytes := make([]byte, 4) + binary.LittleEndian.PutUint32(locktimeBytes, tx.Locktime) + serlzd = append(serlzd, locktimeBytes...) + return serlzd, nil } var ct_p2pkh = 0 @@ -560,208 +564,157 @@ var ct_p2wsh = 0 var ct_p2sh = 0 var ct_p2wpkh = 0 -func Address() { - Directory := "./mempool" - files, err := os.ReadDir(Directory) - handleError(err) - for _, file := range files { - txData, err := readJSON(Directory + "/" + file.Name()) - handleError(err) - var tx Transaction - err = json.Unmarshal([]byte(txData), &tx) - handleError(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 { - ct_p2pkh++ - continue - } else { - fmt.Println("Address not matched") - } - } - - if vin.Prevout.ScriptpubkeyType == "p2sh" { - pubkey_asm := vin.Prevout.ScriptpubkeyAsm - address := P2sh(pubkey_asm) - if string(address) == vin.Prevout.ScriptpubkeyAddress { - ct_p2sh++ - continue - } else { - fmt.Println("Address not matched") - } - } - - if vin.Prevout.ScriptpubkeyType == "v0_p2wpkh" { - pubkey_asm := vin.Prevout.ScriptpubkeyAsm - address := P2wpkh(pubkey_asm) - if string(address) == vin.Prevout.ScriptpubkeyAddress { - ct_p2wpkh++ - continue - } else { - fmt.Println("Address not matched") - } - } - - if vin.Prevout.ScriptpubkeyType == "v0_p2wsh" { - pubkey_asm := vin.Prevout.ScriptpubkeyAsm - address := P2wsh(pubkey_asm) - if string(address) == vin.Prevout.ScriptpubkeyAddress { - ct_p2wsh++ - continue - } else { - fmt.Println("Address not matched") - } - } - } - for _, vout := range tx.Vout { - if vout.ScriptpubkeyType == "p2pkh" { - pubkey_asm := vout.ScriptpubkeyAsm - address := P2pkh(pubkey_asm) - if string(address) == vout.ScriptpubkeyAddress { - ct_p2pkh++ - } else { - fmt.Println("Address not matched") - } - } +func processTransactions() { + directory := "./mempool" + files, err := os.ReadDir(directory) + handleError(err) - if vout.ScriptpubkeyType == "p2sh" { - pubkey_asm := vout.ScriptpubkeyAsm - address := P2sh(pubkey_asm) - if string(address) == vout.ScriptpubkeyAddress { - ct_p2sh++ - continue - } else { - fmt.Println("Address not matched") - } - } - - if vout.ScriptpubkeyType == "v0_p2wpkh" { - pubkey_asm := vout.ScriptpubkeyAsm - address := P2wpkh(pubkey_asm) - if string(address) == vout.ScriptpubkeyAddress { - ct_p2wpkh++ - } else { - fmt.Println("Address not matched") - } - } + for _, file := range files { + txData, err := readJSON(directory + "/" + file.Name()) + handleError(err) - if vout.ScriptpubkeyType == "v0_p2wsh" { - pubkey_asm := vout.ScriptpubkeyAsm - address := P2wsh(pubkey_asm) - if string(address) == vout.ScriptpubkeyAddress { - ct_p2wsh++ - } else { - fmt.Println("Address not matched") + var tx Transaction + err = json.Unmarshal([]byte(txData), &tx) + handleError(err) - } - } - } - } + processInputs(tx.Vin) + processOutputs(tx.Vout) + } } -const ( - versionByte string = "00" -) - -func P2pkh(scriptpubkey_asm string) []byte { - str := strings.Split(scriptpubkey_asm, " ") - - pubkeyhash := extractPubKeyHex(str) - // Convert hex to bytes) - pubkeyhash_bytes, _ := hex.DecodeString(pubkeyhash) - versionByte_bytes, _ := hex.DecodeString(versionByte) - - version_pubkeyhash := append(versionByte_bytes, pubkeyhash_bytes...) - - checksum := sha256Hash(sha256Hash(version_pubkeyhash)) - - appended_checksum := append(version_pubkeyhash, checksum[:4]...) - - address := base58Encode(appended_checksum) - - return address - +func processInputs(inputs []Input) { + for _, input := range inputs { + processAddress(input.Prevout.ScriptpubkeyType, input.Prevout.ScriptpubkeyAsm, input.Prevout.ScriptpubkeyAddress) + } } -func P2sh(scriptpubkey_asm string) []byte { - hashed_script := extractPubKeyHex(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 := sha256Hash(sha256Hash(version_hash)) - - appended_checksum := append(version_hash, checksum[:4]...) - - address := base58Encode(appended_checksum) - - return address - +func processOutputs(outputs []Prevout) { + for _, output := range outputs { + processAddress(output.ScriptpubkeyType, output.ScriptpubkeyAsm, output.ScriptpubkeyAddress) + } } -func P2wpkh(scriptpubkey_asm string) []byte { - - pubkeyHash := extractPubKeyHex(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) - handleError(err) - - versionPubkeyHash := append(versionBytes, conv...) - address, err := bech32.Encode("bc", versionPubkeyHash) - handleError(err) - return []byte(address) +func processAddress(scriptPubKeyType, scriptPubKeyAsm, scriptPubKeyAddress string) { + var address []byte + switch scriptPubKeyType { + case "p2pkh": + address = generateP2PKHAddress(scriptPubKeyAsm) + ct_p2pkh++ + case "p2sh": + address = generateP2SHAddress(scriptPubKeyAsm) + ct_p2sh++ + case "v0_p2wpkh": + address = generateP2WPKHAddress(scriptPubKeyAsm) + ct_p2wpkh++ + case "v0_p2wsh": + address = generateP2WSHAddress(scriptPubKeyAsm) + ct_p2wsh++ + default: + return + } + if string(address) != scriptPubKeyAddress { + fmt.Println("Address not matched") + } } -func P2wsh(scriptpubkey_asm string) []byte { - wit_scrHash := extractPubKeyHex(strings.Split(scriptpubkey_asm, " ")) - wit_scrHash_bytes, _ := hex.DecodeString(wit_scrHash) - version := "00" - version_bytes, _ := hex.DecodeString(version) - - conv, _ := bech32.ConvertBits(wit_scrHash_bytes, 8, 5, true) - conv = append(version_bytes, conv...) +const ( + versionByte string = "00" +) - hrp := "bc" - encodedAddress, _ := bech32.Encode(hrp, conv) - return []byte(encodedAddress) +func doubleHash(data []byte) []byte { + firstHash := sha256.Sum256(data) + secondHash := sha256.Sum256(firstHash[:]) + return secondHash[:] +} + +func generateP2SHAddress(scriptPubKeyAsm string) []byte { + hashedScript := extractPubKeyHex(strings.Split(scriptPubKeyAsm, " ")) + hashedScriptBytes, _ := hex.DecodeString(hashedScript) + versionBytes, _ := hex.DecodeString("05") + versionHash := append(versionBytes, hashedScriptBytes...) + checksum := doubleHash(versionHash)[:4] + addressBytes := append(versionHash, checksum...) + return base58Encode(addressBytes) +} + +func generateP2WSHAddress(scriptPubKeyAsm string) []byte { + witnessScriptHash := extractPubKeyHex(strings.Split(scriptPubKeyAsm, " ")) + witnessScriptHashBytes, _ := hex.DecodeString(witnessScriptHash) + versionBytes, _ := hex.DecodeString("00") + convertedBits, _ := bech32.ConvertBits(witnessScriptHashBytes, 8, 5, true) + encodedAddress, _ := bech32.Encode("bc", append(versionBytes, convertedBits...)) + return []byte(encodedAddress) +} + +func generateP2WPKHAddress(scriptPubKeyAsm string) []byte { + publicKeyHash := extractPubKeyHex(strings.Split(scriptPubKeyAsm, " ")) + publicKeyHashBytes, _ := hex.DecodeString(publicKeyHash) + versionBytes, _ := hex.DecodeString("00") + convertedBits, _ := bech32.ConvertBits(publicKeyHashBytes, 8, 5, true) + encodedAddress, _ := bech32.Encode("bc", append(versionBytes, convertedBits...)) + return []byte(encodedAddress) +} + +func generateP2PKHAddress(scriptPubKeyAsm string) []byte { + publicKeyHash := extractPubKeyHex(strings.Split(scriptPubKeyAsm, " ")) + publicKeyHashBytes, _ := hex.DecodeString(publicKeyHash) + versionBytes, _ := hex.DecodeString(versionByte) + versionPublicKeyHash := append(versionBytes, publicKeyHashBytes...) + checksum := doubleHash(versionPublicKeyHash)[:4] + addressBytes := append(versionPublicKeyHash, checksum...) + return base58Encode(addressBytes) +} + +func calculateTxID(serializedTx []byte) string { + hash := doubleHash(serializedTx) + reversedHash := reverseBytes(hash) + return hex.EncodeToString(reversedHash) +} + +func calculateMerkleRoot(txIDs []string) string { + merkleTree := merkTree(txIDs) + return hex.EncodeToString(merkleTree.Data) +} + +func mineBlock(header *BlockHeader) bool { + return proofOfWork(header) +} + +func writeBlockData(header BlockHeader, coinbaseTx *Transaction, txIDs []string) { + outFile, _ := os.Create("output.txt") + defer outFile.Close() + + serializedBlockHeader := srlzBhead(&header) + segWitCoinbaseTx, _ := SegWitSerialize(coinbaseTx) + + outFile.WriteString(hex.EncodeToString(serializedBlockHeader) + "\n") + outFile.WriteString(hex.EncodeToString(segWitCoinbaseTx) + "\n") + + for _, txID := range txIDs { + outFile.WriteString(txID + "\n") + } } func main() { - networkReward, transactionIDs, _ := Ordering() - - // Create a coinbase transaction for the network reward - coinbaseTx := CreateCoinbase(networkReward) - serlzdCoinbaseTx, _ := serTx(coinbaseTx) - - // Prepend the coinbase transaction ID to the list of transaction IDs - coinbaseTxID := hex.EncodeToString(reverseBytes(sha256Hash(sha256Hash(serlzdCoinbaseTx)))) - transactionIDs = append([]string{coinbaseTxID}, transactionIDs...) - - // Construct a Merkle tree from the transaction IDs - merkleTree := merkTree(transactionIDs) - blockHeader.MerkleRoot = hex.EncodeToString(merkleTree.Data) - - // Perform proof-of-work mining - if proofOfWork(&blockHeader) { - // Create an output file and write block data - outF, _ := os.Create("output.txt") - defer outF.Close() - - serlzdBlockHeader := srlzBhead(&blockHeader) - segWitCoinbaseTx, _ := SegWitSerialize(coinbaseTx) - - outF.WriteString(hex.EncodeToString(serlzdBlockHeader) + "\n") - outF.WriteString(hex.EncodeToString(segWitCoinbaseTx) + "\n") - - for _, txID := range transactionIDs { - outF.WriteString(txID + "\n") - } - } + networkReward, transactionIDs, _ := Ordering() + + // Create a coinbase transaction for the network reward + coinbaseTx := CreateCoinbase(networkReward) + serializedCoinbaseTx, _ := serTx(coinbaseTx) + + // Prepend the coinbase transaction ID to the list of transaction IDs + coinbaseTxID := calculateTxID(serializedCoinbaseTx) + transactionIDs = append([]string{coinbaseTxID}, transactionIDs...) + + // Construct a Merkle tree from the transaction IDs + merkleRoot := calculateMerkleRoot(transactionIDs) + blockHeader.MerkleRoot = merkleRoot + + // Perform proof-of-work mining + if mineBlock(&blockHeader) { + // Create an output file and write block data + writeBlockData(blockHeader, coinbaseTx, transactionIDs) + } } + diff --git a/output.txt b/output.txt index fbd8b32..4b53338 100644 --- a/output.txt +++ b/output.txt @@ -1,4 +1,4 @@ -07000000000000000000000000000000000000000000000000000000000000000000000027a51c6f2c505dda24e565b0827cbcbe8f7a024551a8379896bc363ad84e091e37d22d66ffff001f8f000100 +07000000000000000000000000000000000000000000000000000000000000000000000027a51c6f2c505dda24e565b0827cbcbe8f7a024551a8379896bc363ad84e091ec0d62d66ffff001ffc2d0200 010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff1b03951a0604f15ccf5609013803062b9b5a0100072f425443432f20ffffffff027891860100000000160014df4bf9f3621073202be59ae590f55f42879a21a00000000000000000266a24aa21a9ed857be26be2d48ea615af6abc625860f4a86ed2bef314e57681b02d17efd13bc10120000000000000000000000000000000000000000000000000000000000000000000000000 35638ec7d9e798458871742209c407b7e9111c30955d71bf2f3f7c8fe81dd683 82f9f96db7bdbb9e70626747632e373b34eefd50d613dfea7092744169591b6e