├── .gitignore ├── LICENSE ├── README.md ├── addressEncoder ├── addressCheck.go ├── addressEncoder.go ├── addressEncoder_test.go ├── aeternity.go ├── base32.go ├── base32PolyMod │ └── base32PolyMod.go ├── base58.go ├── base58_test.go ├── base64.go ├── bech32 │ ├── bech32.go │ └── bech32_test.go ├── bech32m │ ├── bech32m.go │ └── bech32m_test.go ├── blake256 │ ├── blake256.go │ └── blake256block.go ├── crc32.go ├── eip55 │ └── eip55.go ├── encoderProfile.go └── eos.go ├── aliencoinTransaction ├── base58.go ├── transaction.go ├── transaction_test.go ├── txIn.go ├── txOut.go ├── txSigPub.go ├── txStruct.go └── txUtil.go ├── bigbangTransaction ├── address.go ├── crc24.go ├── transaction.go ├── transaction_test.go ├── txIn.go ├── txProfile.go ├── txStruct.go └── txUtils.go ├── binancechainTransaction ├── msg-send.go ├── profile.go ├── stdsign.go ├── transaction.go ├── transaction_test.go └── utils.go ├── bitcoincashTransaction ├── SignaturePubkey.go ├── base58.go ├── transaction.go ├── transaction_test.go ├── txHash.go ├── txIn.go ├── txMultiSig.go ├── txOmni.go ├── txOut.go ├── txPayload.go ├── txProfile.go ├── txStruct.go └── txUtils.go ├── bitcoinsvTransaction ├── SignaturePubkey.go ├── base58.go ├── transaction.go ├── transaction_test.go ├── txHash.go ├── txIn.go ├── txMultiSig.go ├── txOmni.go ├── txOut.go ├── txPayload.go ├── txProfile.go ├── txStruct.go └── txUtils.go ├── btcLikeTxDriver ├── README.md ├── base58.go ├── bech32.go ├── btc.go ├── multiSig.go ├── signaturepubkey.go ├── transaction.go ├── transaction_test.go ├── txIn.go ├── txOut.go ├── txStruct.go ├── utils.go └── witness.go ├── btcTransaction ├── README.md ├── SignaturePubkey.go ├── base58.go ├── bech32.go ├── transaction.go ├── transaction_test.go ├── txHash.go ├── txIn.go ├── txMultiSig.go ├── txOut.go ├── txProfile.go ├── txStruct.go └── txUtils.go ├── bytomTransaction ├── README.md ├── bech32.go ├── sigPub.go ├── transaction.go ├── transaction_test.go ├── txHash.go ├── txIn.go ├── txMultiSig.go ├── txOut.go ├── txProfile.go ├── txStruct.go └── txUtil.go ├── cosmosTransaction ├── bech32.go ├── bech32_test.go ├── coin.go ├── fee.go ├── json_test.go ├── message.go ├── transaction.go ├── transaction_test.go ├── txSend.go ├── txStruct.go └── utils.go ├── cxcTransaction ├── README.md ├── SignaturePubkey.go ├── base58.go ├── bech32.go ├── transaction.go ├── transaction_test.go ├── txAsset.go ├── txHash.go ├── txIn.go ├── txMultiSig.go ├── txOut.go ├── txProfile.go ├── txStruct.go └── txUtils.go ├── elastosTransaction ├── base58.go ├── transaction.go ├── transaction_test.go ├── txIn.go ├── txOut.go ├── txProfile.go ├── txSignature.go ├── txStruct.go └── txUtils.go ├── eosSignature ├── randomGenerator.go ├── signature.go └── signature_test.go ├── evaioTransaction ├── bech32.go ├── bech32_test.go ├── coin.go ├── fee.go ├── json_test.go ├── message.go ├── transaction.go ├── transaction_test.go ├── txSend.go ├── txStruct.go └── utils.go ├── example_owkeychain.go ├── fiiiTransaction ├── base58.go ├── transaction.go ├── transaction_test.go ├── txInput.go ├── txOutput.go ├── txStruct.go └── txUnlock.go ├── filenetTransaction ├── address.go ├── address_test.go ├── base58.go ├── transaction.go ├── transaction_test.go ├── txStruct.go ├── txTo.go └── txUtils.go ├── go.mod ├── hcTransaction ├── README.md ├── SignaturePubkey.go ├── base58.go ├── base58_test.go ├── bech32.go ├── transaction.go ├── transaction_test.go ├── txHash.go ├── txIn.go ├── txMultiSig.go ├── txOut.go ├── txProfile.go ├── txStruct.go └── txUtils.go ├── hypercashTransaction ├── TxStruct.go ├── base58.go ├── omni_test.go ├── transaction.go ├── transaction_test.go ├── txIn.go ├── txOmni.go ├── txOut.go ├── txProfile.go ├── txSignature.go └── txUtils.go ├── mateverseTransaction ├── transaction.go ├── transaction_test.go ├── txHash.go ├── txIn.go ├── txProfile.go ├── txSigPub.go └── txUtils.go ├── moacchainTransaction ├── transaction.go ├── transaction_test.go ├── txProfile.go ├── txStruct.go └── txUtils.go ├── omniTransaction ├── README.md ├── SignaturePubkey.go ├── base58.go ├── bech32.go ├── transaction.go ├── transaction_test.go ├── txHash.go ├── txIn.go ├── txMultiSig.go ├── txOmni.go ├── txOut.go ├── txPayload.go ├── txProfile.go ├── txStruct.go └── txUtils.go ├── ontologyTransaction ├── README.md ├── base58.go ├── transaction.go ├── transaction_test.go ├── txHash.go ├── txInvokeCode.go ├── txProfile.go ├── txSigData.go ├── txSigPub.go ├── txStruct.go └── txUtils.go ├── owkeychain ├── base58.go ├── extendedkey.go ├── extendedkey_test.go ├── hdkey.go ├── hdprofile.go ├── multisig.go ├── multisig_test.go └── owencode.go ├── polkadotTransaction ├── codec.go ├── codec_test.go ├── transaction.go ├── transaction_test.go ├── txEra.go ├── txEra_test.go ├── txMethod.go ├── txPayLoad.go ├── txProfile.go └── txStruct.go ├── rippleTransaction ├── base58.go ├── enc_test.go ├── transaction.go ├── transaction_test.go ├── txEnc.go ├── txProfile.go ├── txStruct.go └── txUtils.go ├── signatureSet ├── ethSignature.go ├── icxSignature.go ├── init.go ├── nasSignature.go ├── signature.go ├── signatureSet_test.go ├── tronSignature.go └── vsysSignature.go ├── vergeTransaction ├── base58.go ├── transaction.go ├── transaction_test.go ├── txIn.go ├── txOut.go ├── txSigPub.go ├── txStruct.go └── txUtil.go ├── virtualeconomyTransaction ├── base58.go ├── transaction.go ├── transaction_test.go ├── txDecode.go └── txProfile.go ├── vollarTransaction ├── base58.go ├── transaction.go ├── transaction_test.go ├── txIn.go ├── txOut.go ├── txSigPub.go ├── txStruct.go └── txUtil.go ├── waykichainTransaction ├── base58.go ├── transaction.go ├── transaction_test.go ├── txProfile.go ├── txSignature.go ├── txStruct.go └── txUtils.go └── zencashTransaction ├── SignaturePubkey.go ├── base58.go ├── base58_test.go ├── bech32.go ├── transaction.go ├── transaction_test.go ├── txHash.go ├── txIn.go ├── txMultiSig.go ├── txOut.go ├── txProfile.go ├── txStruct.go └── txUtils.go /.gitignore: -------------------------------------------------------------------------------- 1 | go.sum 2 | .idea 3 | -------------------------------------------------------------------------------- /addressEncoder/aeternity.go: -------------------------------------------------------------------------------- 1 | package addressEncoder 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | const ( 8 | AEPrefixAccountPubkey = "ak_" 9 | ) 10 | 11 | func decodeAE(pubKey string, addresstype AddressType) ([]byte, error) { 12 | 13 | var pubKeyMaterial string 14 | if strings.HasPrefix(pubKey, AEPrefixAccountPubkey) { 15 | pubKeyMaterial = pubKey[len(AEPrefixAccountPubkey):] 16 | } 17 | 18 | ret, err := Base58Decode(pubKeyMaterial, NewBase58Alphabet(addresstype.Alphabet)) 19 | if err != nil { 20 | return nil, ErrorInvalidAddress 21 | } 22 | if verifyChecksum(ret, addresstype.ChecksumType) == false { 23 | return nil, ErrorInvalidAddress 24 | } 25 | 26 | return ret[:len(ret)-4], nil 27 | } 28 | 29 | func encodeAE(hash []byte, addresstype AddressType) string { 30 | addresstype.EncodeType = "base58" 31 | data := catData(hash, calcChecksum(hash, addresstype.ChecksumType)) 32 | return string(addresstype.Prefix) + encodeData(data, "base58", addresstype.Alphabet) 33 | } 34 | -------------------------------------------------------------------------------- /addressEncoder/base32.go: -------------------------------------------------------------------------------- 1 | package addressEncoder 2 | -------------------------------------------------------------------------------- /addressEncoder/base58_test.go: -------------------------------------------------------------------------------- 1 | package addressEncoder 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "github.com/blocktree/go-owcdrivers/addressEncoder/bech32" 7 | "testing" 8 | ) 9 | 10 | func TestBase58Decode(t *testing.T) { 11 | addr := "VcbvpFHqiwqbsYvaeP71Jw3a11gbCiVyPyU" 12 | hash, err := Base58Decode(addr, NewBase58Alphabet(BTCAlphabet)) 13 | if err != nil { 14 | t.Errorf("unexpected error: %v", err) 15 | return 16 | } 17 | fmt.Printf("hash: %s \n", hex.EncodeToString(hash)) 18 | 19 | } 20 | 21 | 22 | func TestBech32(t *testing.T) { 23 | hash,_ := hex.DecodeString("09ca6d8f32d802edd899a894172c6ea966a612c9") 24 | prefix := "bnb" 25 | 26 | addr := bech32.Encode(prefix, BTCBech32Alphabet, hash, nil) 27 | fmt.Println(addr) 28 | } -------------------------------------------------------------------------------- /addressEncoder/bech32/bech32_test.go: -------------------------------------------------------------------------------- 1 | package bech32 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_bech32_address(t *testing.T) { 10 | address := "bc1qvgclzqz7smqr6haag9mknpwsjnxtdqkncr64kd" 11 | ret, err := Decode(address, "qpzry9x8gf2tvdw0s3jn54khce6mua7l") 12 | if err != nil { 13 | t.Error("decode error") 14 | } else { 15 | fmt.Println(hex.EncodeToString(ret)) 16 | } 17 | 18 | addresschk := Encode("bc", "qpzry9x8gf2tvdw0s3jn54khce6mua7l", ret, nil) 19 | if addresschk != address { 20 | t.Error("encode error") 21 | } else { 22 | fmt.Println(addresschk) 23 | } 24 | 25 | } 26 | 27 | func Test_xch(t *testing.T) { 28 | address := "xch1ajutskhwh0rm06cmxatp4e7d2zzffe4f46mkv3ldz93w46jzsxkqd3rtq6" 29 | ret, err := Decode(address, "qpzry9x8gf2tvdw0s3jn54khce6mua7l") 30 | 31 | fmt.Println(hex.EncodeToString(ret)) 32 | fmt.Println(err) 33 | } -------------------------------------------------------------------------------- /addressEncoder/bech32m/bech32m_test.go: -------------------------------------------------------------------------------- 1 | package bech32m 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_Encode(t *testing.T) { 10 | hash, _ := hex.DecodeString("ecb8b85aeebbc7b7eb1b37561ae7cd508494e6a9aeb76647ed1162eaea4281ac") 11 | excepted := "xch1ajutskhwh0rm06cmxatp4e7d2zzffe4f46mkv3ldz93w46jzsxkqd3rtq6" 12 | prefix := "xch" 13 | charset := "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 14 | 15 | address, err := Bech32mEncode(prefix, hash, charset) 16 | if err != nil { 17 | t.Error("decode error") 18 | return 19 | } else { 20 | if address != excepted { 21 | t.Error("result error") 22 | return 23 | } 24 | fmt.Println("SUCCESS") 25 | } 26 | } 27 | 28 | func Test_Decode(t *testing.T) { 29 | address := "xch1ajutskhwh0rm06cmxatp4e7d2zzffe4f46mkv3ldz93w46jzsxkqd3rtq6" 30 | prefix := "xch" 31 | charset := "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 32 | excepted := "ecb8b85aeebbc7b7eb1b37561ae7cd508494e6a9aeb76647ed1162eaea4281ac" 33 | hash, err := Bech32mDecode(address, prefix,charset) 34 | if err != nil { 35 | t.Error("decode error") 36 | return 37 | } else { 38 | if hex.EncodeToString(hash) != excepted { 39 | t.Error("result error") 40 | return 41 | } 42 | fmt.Println("SUCCESS") 43 | } 44 | } 45 | 46 | 47 | 48 | func Test_Encode_chia_testnet(t *testing.T) { 49 | hash, _ := hex.DecodeString("ea2efc3186f7117223dd8f5552810a2fa5d1ad0c6385e4031bad34d421c027a5") 50 | prefix := "txch" 51 | charset := "qpzry9x8gf2tvdw0s3jn54khce6mua7l" 52 | 53 | address, err := Bech32mEncode(prefix, hash, charset) 54 | if err != nil { 55 | t.Error("decode error") 56 | return 57 | } else { 58 | fmt.Println("SUCCESS") 59 | fmt.Println(address) 60 | } 61 | } -------------------------------------------------------------------------------- /addressEncoder/crc32.go: -------------------------------------------------------------------------------- 1 | package addressEncoder 2 | 3 | import( 4 | "encoding/binary" 5 | ) 6 | 7 | func CRC32(s []byte) []byte { 8 | var table [256]uint32 9 | for i := range table { 10 | word := uint32(i) 11 | for j := 0; j < 8; j++ { 12 | if word&1 == 1 { 13 | word = (word >> 1) ^ 0xedb88320 14 | } else { 15 | word >>= 1 16 | } 17 | } 18 | table[i] = word 19 | } 20 | crc := ^uint32(0) 21 | crcbuf:=make([]byte,4) 22 | for i := 0; i < len(s); i++ { 23 | crc = table[byte(crc)^s[i]] ^ (crc >> 8) 24 | } 25 | binary.BigEndian.PutUint32(crcbuf,^crc) 26 | return crcbuf 27 | } -------------------------------------------------------------------------------- /addressEncoder/eip55/eip55.go: -------------------------------------------------------------------------------- 1 | package eip55 2 | 3 | import( 4 | "errors" 5 | "encoding/hex" 6 | ) 7 | var ( 8 | ErrorInvalidAddress = errors.New("Invalid address!") 9 | ) 10 | 11 | func byte_to_half(in[]byte, in_len int)[]byte{ 12 | out := make([]byte, in_len<<1) 13 | for i:=0;i>4)&0x0f 15 | out[2*i+1]=in[i]&0x0f 16 | } 17 | return out 18 | } 19 | 20 | func hex_to_str(inchar[]uint8,inchar_len int)[]byte{ 21 | hbit:=byte(1) 22 | lbit:=byte(1) 23 | out :=make([]byte,inchar_len<<1) 24 | 25 | i:=0 26 | for ;i>4 28 | lbit=inchar[i]&0x0f 29 | if hbit>9{ 30 | out[2*i]='a'+hbit-10 31 | }else{ 32 | out[2*i]='0'+hbit 33 | } 34 | 35 | if lbit>9{ 36 | out[2*i+1]='a'+lbit-10 37 | }else{ 38 | out[2*i+1]='0'+lbit 39 | } 40 | } 41 | //out[2*i]=0 42 | return out 43 | } 44 | 45 | 46 | func Eip55_encode(addr[]byte)string{ 47 | /* 48 | encode_addr :=make([]byte,40) 49 | addr_hex:=make([]byte,40) 50 | knecck256:=make([]byte,32) 51 | knecck256_hex:=make([]byte,64) 52 | addr_hex=hex_to_str(addr[:],20) 53 | knecck256=owcrypt.Hash(addr_hex, 0, owcrypt.HASH_ALG_KECCAK256) 54 | knecck256_hex=byte_to_half(knecck256[:], 32) 55 | for i:=0;i<40;i++{ 56 | if((addr_hex[i]>=48)&&(addr_hex[i]<=57)){ 57 | encode_addr[i]=addr_hex[i] 58 | }else{ 59 | if knecck256_hex[i]>=8{ 60 | encode_addr[i]=addr_hex[i]-32 61 | }else{ 62 | encode_addr[i]=addr_hex[i] 63 | } 64 | } 65 | } 66 | str:=string(encode_addr) 67 | */ 68 | encode_addr:=addr[12:] 69 | str :=hex.EncodeToString(encode_addr[:]) 70 | //str :=string(encode_addr[:]) 71 | return str 72 | } 73 | 74 | func Eip55_decode(encode_addr string)([]byte,error){ 75 | /* 76 | //according to eip55 77 | decode_addr:=make([]byte,20) 78 | var check_addr string 79 | for _,ch:=range encode_addr{ 80 | if (ch < 48)||((ch>57) &&(ch<65))||((ch>70)&&(ch<97))||(ch>102){ 81 | return nil,ErrorInvalidAddress 82 | } 83 | } 84 | decode_addr,err:= hex.DecodeString(encode_addr[:]) 85 | if err!=nil{ 86 | 87 | return nil,err 88 | } 89 | check_addr=Eip55_encode(decode_addr[:]) 90 | ret:=strings.Compare(encode_addr,check_addr) 91 | if ret==-1{ 92 | return nil,ErrorInvalidAddress 93 | } 94 | */ 95 | if encode_addr[0:2] == "0x" { 96 | encode_addr = encode_addr[2:] 97 | } 98 | decode_addr,err :=hex.DecodeString(encode_addr) 99 | if err!=nil{ 100 | return nil,err 101 | } 102 | return decode_addr,err 103 | } 104 | 105 | -------------------------------------------------------------------------------- /addressEncoder/eos.go: -------------------------------------------------------------------------------- 1 | package addressEncoder 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | EOSPublicKeyPrefix = "PUB_" 10 | EOSPublicKeyK1Prefix = "PUB_K1_" 11 | EOSPublicKeyR1Prefix = "PUB_R1_" 12 | EOSPublicKeyPrefixCompat = "EOS" 13 | ) 14 | 15 | func decodeEOS(pubKey string, addresstype AddressType) ([]byte, error) { 16 | 17 | var pubKeyMaterial string 18 | if strings.HasPrefix(pubKey, EOSPublicKeyR1Prefix) { 19 | pubKeyMaterial = pubKey[len(EOSPublicKeyR1Prefix):] // strip "PUB_R1_" 20 | } else if strings.HasPrefix(pubKey, EOSPublicKeyK1Prefix) { 21 | pubKeyMaterial = pubKey[len(EOSPublicKeyK1Prefix):] // strip "PUB_K1_" 22 | } else if strings.HasPrefix(pubKey, EOSPublicKeyPrefixCompat) { // "EOS" 23 | pubKeyMaterial = pubKey[len(EOSPublicKeyPrefixCompat):] // strip "EOS" 24 | } else { 25 | return nil, fmt.Errorf("public key should start with [%q | %q] (or the old %q)", EOSPublicKeyK1Prefix, EOSPublicKeyR1Prefix, EOSPublicKeyPrefixCompat) 26 | } 27 | 28 | ret, err := Base58Decode(pubKeyMaterial, NewBase58Alphabet(addresstype.Alphabet)) 29 | if err != nil { 30 | return nil, ErrorInvalidAddress 31 | } 32 | if verifyChecksum(ret, addresstype.ChecksumType) == false { 33 | return nil, ErrorInvalidAddress 34 | } 35 | 36 | return ret[:len(ret)-4], nil 37 | } 38 | 39 | func encodeEOS(hash []byte, addresstype AddressType) string { 40 | addresstype.EncodeType = "base58" 41 | data := catData(hash, calcChecksum(hash, addresstype.ChecksumType)) 42 | return string(addresstype.Prefix) + encodeData(data, "base58", addresstype.Alphabet) 43 | } -------------------------------------------------------------------------------- /aliencoinTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package aliencoinTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | 7 | owcrypt "github.com/blocktree/go-owcrypt" 8 | ) 9 | 10 | type Vin struct { 11 | TxID string 12 | Vout uint32 13 | LockScript string 14 | } 15 | 16 | type Vout struct { 17 | Address string 18 | Amount uint64 19 | } 20 | 21 | func CreateEmptyTransactionAndHash(vin []Vin, vout []Vout, lockTime uint32) (string, []string, error) { 22 | txStruct, err := NewTxStruct(vin, vout, lockTime) 23 | if err != nil { 24 | return "", nil, err 25 | } 26 | 27 | hashes, _ := txStruct.GetHash() 28 | 29 | txHex := hex.EncodeToString(txStruct.ToBytes()) 30 | for _, in := range vin { 31 | txHex += ":" + in.LockScript 32 | } 33 | 34 | return txHex, hashes, nil 35 | } 36 | 37 | func SignTransaction(hash string, prikey []byte) ([]byte, error) { 38 | hashBytes, err := hex.DecodeString(hash) 39 | if err != nil { 40 | return nil, errors.New("Invalid hash!") 41 | } 42 | 43 | signature,_, retCode := owcrypt.Signature(prikey, nil, hashBytes, owcrypt.ECC_CURVE_SECP256K1) 44 | if retCode != owcrypt.SUCCESS { 45 | return nil, errors.New("Sign Failed!") 46 | } 47 | signature = serilizeS(signature) 48 | return signature, nil 49 | } 50 | 51 | func VerifyAndCombineTransaction(emptyTrans string, sigPubs []SigPub) (bool, string, error) { 52 | 53 | txStruct, _, err := DecodeTxStructRaw(emptyTrans) 54 | if err != nil { 55 | return false, "", err 56 | } 57 | if sigPubs == nil || len(sigPubs) == 0 || len(sigPubs) != len(txStruct.Vin) { 58 | return false, "", errors.New("inputs and signatures not match!") 59 | } 60 | 61 | hashes, err := txStruct.GetHash() 62 | if err != nil { 63 | return false, "", err 64 | } 65 | for index := 0; index < len(sigPubs); index++ { 66 | hashBytes, _ := hex.DecodeString(hashes[index]) 67 | pubkey := owcrypt.PointDecompress(sigPubs[index].Pubkey, owcrypt.ECC_CURVE_SECP256K1)[1:] 68 | if owcrypt.SUCCESS != owcrypt.Verify(pubkey, nil, hashBytes, sigPubs[index].Signature, owcrypt.ECC_CURVE_SECP256K1) { 69 | return false, "", errors.New("verify transaction failed!") 70 | } 71 | txStruct.Vin[index].SigPub = &sigPubs[index] 72 | } 73 | 74 | return true, hex.EncodeToString(txStruct.ToBytes()), nil 75 | } 76 | -------------------------------------------------------------------------------- /aliencoinTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package aliencoinTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_send(t *testing.T) { 10 | // 该案例参照链上交易 03e5032a0b99dbea17e9f3665d7dee22b4632f8cdd6150fed8c87dbc485f36f3 进行构建 11 | 12 | txid := "03e5032a0b99dbea17e9f3665d7dee22b4632f8cdd6150fed8c87dbc485f36f3" 13 | vout := uint32(0) 14 | lockScript := "76a914e6abcb20945486c8c5432e518d29d140f3c0daeb88ac" 15 | 16 | to1 := "ASoPDMzv6C6ZkZaeYCFMaHgGzSUKezRze2" 17 | amount1 := uint64(31798399988116) 18 | to2 := "AQhxgsCGDUq8oiWCK96RAaeDyJmQ924irK" 19 | amount2 := uint64(100000000000) 20 | 21 | // 构建输入 22 | vins := []Vin{Vin{txid, vout, lockScript}} 23 | // 构建输出 24 | vouts := []Vout{Vout{to1, amount1}, Vout{to2, amount2}} 25 | 26 | //其他参数 27 | lockTime := uint32(0) 28 | 29 | // 构建交易单和待签哈希 30 | emptyTrans, hashes, err := CreateEmptyTransactionAndHash(vins, vouts, lockTime) 31 | if err != nil { 32 | t.Error("create failed!") 33 | return 34 | } else { 35 | fmt.Println("空交易单:\n", emptyTrans) 36 | fmt.Println("待签哈希:\n", hashes[0]) 37 | } 38 | 39 | // 对交易单签名 40 | prikey := []byte{0x84, 0x98, 0x23, 0xd2, 0x2d, 0x81, 0xe4, 0x9e, 0xb7, 0x19, 0x06, 0x6b, 0xcf, 0x7e, 0xd1, 0x73, 0xe6, 0x09, 0x48, 0x22, 0xb0, 0xea, 0x4e, 0x79, 0x3f, 0x1d, 0x85, 0x97, 0xa5, 0x06, 0x0d, 0x27} 41 | signature, err := SignTransaction(hashes[0], prikey) 42 | if err != nil { 43 | t.Error("failed to sign!") 44 | } else { 45 | // only for test 46 | signature, _ = hex.DecodeString("178d596dff077d60aeedd654b611e0e186211e0a2f776d22e28008a7694f197d3250f8d4f99a70cb7bf92bb3b3aaa3925115749b6ce1c4f2d8b03935f43d5c4f") 47 | fmt.Println("签名结果:\n", hex.EncodeToString(signature)) 48 | } 49 | 50 | // 验证合并 51 | pubkey, _ := hex.DecodeString("03561fe30b5a25d4b458bb65394a84061978924938914d688debbe52ebfcdadbba") 52 | 53 | //构建签名 54 | sigPubs := []SigPub{SigPub{pubkey, signature}} 55 | 56 | pass, signedTrans, err := VerifyAndCombineTransaction(emptyTrans, sigPubs) 57 | if err != nil { 58 | t.Error("failed to verify!") 59 | } else { 60 | if pass != true { 61 | fmt.Println("verify failed!") 62 | } else { 63 | fmt.Println("待发送交易单:\n", signedTrans) 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /aliencoinTransaction/txIn.go: -------------------------------------------------------------------------------- 1 | package aliencoinTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | type TxIn struct { 10 | TxID []byte 11 | Vout []byte 12 | SigPub *SigPub 13 | Sequence []byte 14 | LockScript []byte 15 | } 16 | 17 | func (in Vin) NewTxIn() (*TxIn, error) { 18 | txidBytes, err := reverseHexToBytes(in.TxID) 19 | if err != nil { 20 | return nil, errors.New("Invalid txid!") 21 | } 22 | 23 | vout := make([]byte, 4) 24 | binary.LittleEndian.PutUint32(vout[:], in.Vout) 25 | lockScript, err := hex.DecodeString(in.LockScript) 26 | if err != nil { 27 | return nil, errors.New("invalid lock script!") 28 | } 29 | lockScript = append([]byte{byte(len(lockScript))}, lockScript...) 30 | return &TxIn{ 31 | TxID: txidBytes, 32 | Vout: vout, 33 | SigPub: nil, 34 | Sequence: []byte{0xff, 0xff, 0xff, 0xff}, 35 | LockScript: lockScript, 36 | }, nil 37 | } 38 | 39 | func (in TxIn) ToBytes() []byte { 40 | ret := []byte{} 41 | ret = append(ret, in.TxID...) 42 | ret = append(ret, in.Vout...) 43 | if in.SigPub == nil { 44 | ret = append(ret, byte(0)) 45 | } else { 46 | ret = append(ret, in.SigPub.ToBytes()...) 47 | } 48 | ret = append(ret, in.Sequence...) 49 | return ret 50 | } 51 | -------------------------------------------------------------------------------- /aliencoinTransaction/txOut.go: -------------------------------------------------------------------------------- 1 | package aliencoinTransaction 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | const ( 8 | p2pkhPrefix = byte(0x17) 9 | p2shPrefix = byte(0x30) 10 | ) 11 | 12 | type TxOut struct { 13 | Amount []byte 14 | LockScript []byte 15 | } 16 | 17 | func (out Vout) NewTxOut() (*TxOut, error) { 18 | 19 | prefix, lockScript, err := DecodeCheck(out.Address) 20 | 21 | if err != nil { 22 | return nil, errors.New("Invalid address to send!") 23 | } 24 | 25 | if prefix == p2pkhPrefix { 26 | lockScript = append([]byte{0x19, 0x76, 0xa9, 0x14}, lockScript...) 27 | lockScript = append(lockScript, []byte{0x88, 0xAC}...) 28 | } else if prefix == p2shPrefix { 29 | lockScript = append([]byte{0x17, 0xa9, 0x14}, lockScript...) 30 | lockScript = append(lockScript, byte(0x87)) 31 | } else { 32 | return nil, errors.New("Unsupport address to send!") 33 | } 34 | 35 | return &TxOut{ 36 | Amount: uint64ToLittleEndianBytes(out.Amount), 37 | LockScript: lockScript, 38 | }, nil 39 | } 40 | 41 | func (out TxOut) ToBytes() []byte { 42 | ret := []byte{} 43 | ret = append(ret, out.Amount...) 44 | ret = append(ret, out.LockScript...) 45 | return ret 46 | } 47 | -------------------------------------------------------------------------------- /aliencoinTransaction/txUtil.go: -------------------------------------------------------------------------------- 1 | package aliencoinTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | //reverseBytes endian reverse 10 | func reverseBytes(s []byte) []byte { 11 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 12 | s[i], s[j] = s[j], s[i] 13 | } 14 | return s 15 | } 16 | 17 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 18 | func reverseHexToBytes(hexVar string) ([]byte, error) { 19 | if len(hexVar)%2 == 1 { 20 | return nil, errors.New("Invalid TxHash!") 21 | } 22 | ret, err := hex.DecodeString(hexVar) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return reverseBytes(ret), nil 27 | } 28 | 29 | //uint64ToLittleEndianBytes 30 | func uint64ToLittleEndianBytes(data uint64) []byte { 31 | tmp := [8]byte{} 32 | binary.LittleEndian.PutUint64(tmp[:], data) 33 | return tmp[:] 34 | } 35 | 36 | //littleEndianBytesToUint64 37 | func littleEndianBytesToUint64(data []byte) uint64 { 38 | return binary.LittleEndian.Uint64(data) 39 | } 40 | 41 | //uint64ToLittleEndianBytes 42 | func uint32ToLittleEndianBytes(data uint32) []byte { 43 | tmp := [4]byte{} 44 | binary.LittleEndian.PutUint32(tmp[:], data) 45 | return tmp[:] 46 | } 47 | -------------------------------------------------------------------------------- /bigbangTransaction/address.go: -------------------------------------------------------------------------------- 1 | package bigbangTransaction 2 | 3 | import ( 4 | "encoding/base32" 5 | "errors" 6 | ) 7 | 8 | func addressDecode(address string) ([]byte, error) { 9 | if address[:1] != pubkeyPrefix { 10 | return nil, errors.New("Invalid address, only public key address supported!") 11 | } 12 | 13 | pubkey, err := base32.NewEncoding(alphaBet).DecodeString(address[1:]) 14 | 15 | if err != nil || len(pubkey) != 32 + 3 { 16 | return nil, errors.New("Invalid address!") 17 | } 18 | 19 | chkBytes := uint32ToBigEndianBytes(crc24q(pubkey[:32]))[1:] 20 | for i := 0; i < 3; i ++ { 21 | if chkBytes[i] != pubkey[32 + i] { 22 | return nil, errors.New("Invalid address with bad checksum!") 23 | } 24 | } 25 | 26 | return append([]byte{byte(pubkeyPrefix[0] - '0')}, pubkey[:32]...), nil 27 | } 28 | -------------------------------------------------------------------------------- /bigbangTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package bigbangTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "github.com/blocktree/go-owcrypt" 7 | "time" 8 | ) 9 | 10 | type Vin struct { 11 | TxID string 12 | Vout byte 13 | } 14 | 15 | func CreateEmptyTransactionAndHash(lockUntil uint32, anchor string, inputs []Vin, to string, amount, fee uint64, data string) (string, string, error) { 16 | timestamp := uint32(time.Now().Unix()) 17 | 18 | tx, err := NewTxStruct(DefaultVersion, TxType_Token, timestamp, lockUntil, anchor, inputs, to, amount, fee, data) 19 | if err != nil { 20 | return "", "", err 21 | } 22 | 23 | return hex.EncodeToString(tx.ToBytes()), hex.EncodeToString(tx.GetHash()), nil 24 | } 25 | 26 | func SignTransactionHash(hash string, prikey []byte) (string, error) { 27 | hashBytes, err := hex.DecodeString(hash) 28 | if err != nil || len(hashBytes) != 32 { 29 | return "", errors.New("Invalid transaction hex string!") 30 | } 31 | 32 | if prikey == nil || len(prikey) != 32 { 33 | return "", errors.New("Invalid prikey data!") 34 | } 35 | 36 | signature,_, retCode := owcrypt.Signature(prikey, nil, hashBytes, owcrypt.ECC_CURVE_ED25519) 37 | 38 | if retCode != owcrypt.SUCCESS { 39 | return "", errors.New("Transaction sign failed") 40 | } 41 | 42 | return hex.EncodeToString(signature), nil 43 | } 44 | 45 | func VerifyAndCombineTransaction(emptyTrans, signature string, pubkey []byte) (bool, string) { 46 | trans, err := hex.DecodeString(emptyTrans) 47 | if err != nil || len(trans) == 0 { 48 | return false, "" 49 | } 50 | 51 | sig, err := hex.DecodeString(signature) 52 | if err != nil || len(sig) != 64 { 53 | return false, "" 54 | } 55 | 56 | if pubkey == nil || len(pubkey) != 32 { 57 | return false, "" 58 | } 59 | 60 | if owcrypt.SUCCESS != owcrypt.Verify(pubkey, nil, owcrypt.Hash(trans, 32, owcrypt.HASH_ALG_BLAKE2B), sig, owcrypt.ECC_CURVE_ED25519) { 61 | return false, "" 62 | } 63 | 64 | trans = append(trans, byte(0x40)) 65 | trans = append(trans, sig...) 66 | 67 | return true, hex.EncodeToString(trans) 68 | } -------------------------------------------------------------------------------- /bigbangTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package bigbangTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | // cheange timestamp in CreateEmptyTransactionAndHash() to 1572920900 to pass this case 10 | func Test_testnet_5dc0de44ec87306611fbb5af303ffc96d167d2a5a3fd75bd1e4a865a01a65b58(t *testing.T) { 11 | anchor := "00000000dcde418dca150e49f53ab857d5ccd095a800bfb29ea87385267bc069" 12 | txid := "5dba7e87b7060a1cab84d13700e17519b5cde33d2aaa466d45c519d5e125dada" 13 | vout := byte(0) 14 | 15 | vin := Vin{ 16 | TxID: txid, 17 | Vout: vout, 18 | } 19 | 20 | to := "1j3xa8kka2d0y1ep3x7dadvkwy771aa02h791029t4sqhgn4j8c3xysst" 21 | amount := uint64(1000000) 22 | fee := uint64(100) 23 | 24 | memo := "" 25 | 26 | lockUntil := uint32(0) 27 | 28 | emptyTrans, hash, err := CreateEmptyTransactionAndHash(lockUntil, anchor, []Vin{vin}, to, amount, fee, memo) 29 | 30 | if err != nil { 31 | fmt.Println(err) 32 | t.Error("Create failed!") 33 | return 34 | } else { 35 | fmt.Println("Empty transaction : ", emptyTrans) 36 | fmt.Println("Transaction hash : ", hash) 37 | } 38 | 39 | prikey := []byte{0x80, 0xbc, 0x39, 0x8d, 0x7c, 0x4a, 0x67, 0x4d, 0xaa, 0x97, 0x75, 0x66, 0xc2, 0xe6, 0xcd, 0x50, 0x40, 0x52, 0x00, 0x27, 0xe5, 0x7f, 0xe8, 0x06, 0xdf, 0xaa, 0x86, 0x8d, 0xf4, 0xcc, 0x43, 0xab} 40 | signature, err := SignTransactionHash(hash, prikey) 41 | if err != nil { 42 | fmt.Println(err) 43 | t.Error("Sign failed!") 44 | return 45 | } else { 46 | signature = "d8b31d2a6d7ed3c6b24be66284491bc79f5635de0e1f8aa974cea8a15071f4cef2db71735381be6c23d86bebd22985bccf75f1886ea93fe4d74aadc13307de02" 47 | fmt.Println("Signature result : ", signature) 48 | } 49 | 50 | pubkey, _ := hex.DecodeString("43c814013916d558d23f183ad85a16d947448518ccf91f4591493a49be8f4f64") 51 | 52 | pass, signedTrans := VerifyAndCombineTransaction(emptyTrans, signature, pubkey) 53 | if pass { 54 | fmt.Println("success") 55 | fmt.Println("Signed transaction : ", signedTrans) 56 | } else { 57 | t.Error("Verify failed!") 58 | return 59 | } 60 | 61 | } -------------------------------------------------------------------------------- /bigbangTransaction/txIn.go: -------------------------------------------------------------------------------- 1 | package bigbangTransaction 2 | 3 | import "errors" 4 | 5 | type TxIn struct { 6 | TxID []byte 7 | Vout byte 8 | } 9 | 10 | func NewTxIn(txid string, vout byte) (*TxIn, error) { 11 | if txid == "" || len(txid) != 64 { 12 | return nil, errors.New("Invalid txid!") 13 | } 14 | 15 | txidBytes, err := reverseHexToBytes(txid) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return &TxIn{ 21 | TxID: txidBytes, 22 | Vout: vout, 23 | }, nil 24 | } 25 | 26 | func (id TxIn) ToBytes() []byte { 27 | return append(id.TxID, id.Vout) 28 | } 29 | -------------------------------------------------------------------------------- /bigbangTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package bigbangTransaction 2 | 3 | const ( 4 | DefaultVersion = uint16(1) 5 | TxType_Token = uint16(0) 6 | DefaultLockUntil = uint32(0) 7 | ) 8 | 9 | const ( 10 | alphaBet = "0123456789abcdefghjkmnpqrstvwxyz" 11 | pubkeyPrefix = "1" 12 | ) -------------------------------------------------------------------------------- /bigbangTransaction/txStruct.go: -------------------------------------------------------------------------------- 1 | package bigbangTransaction 2 | 3 | import ( 4 | "errors" 5 | "github.com/blocktree/go-owcrypt" 6 | ) 7 | 8 | type TxStruct struct{ 9 | Version []byte 10 | Type []byte 11 | Time []byte 12 | LockUntil []byte 13 | Anchor []byte 14 | Vins []*TxIn 15 | To []byte 16 | Amount []byte 17 | Fee []byte 18 | Data []byte 19 | } 20 | 21 | func NewTxStruct(version, txtype uint16, timestamp, lockUntil uint32, anchor string, inputs []Vin, to string, amount, fee uint64, data string) (*TxStruct, error) { 22 | var tx TxStruct 23 | 24 | tx.Version = uint16ToLittleEndianBytes(version) 25 | tx.Type = uint16ToLittleEndianBytes(txtype) 26 | 27 | tx.Time = uint32ToLittleEndianBytes(timestamp) 28 | tx.LockUntil = uint32ToLittleEndianBytes(lockUntil) 29 | 30 | anchorBytes, err := reverseHexToBytes(anchor) 31 | if err != nil { 32 | return nil, errors.New("Invalid anchor string!") 33 | } 34 | tx.Anchor = anchorBytes 35 | 36 | if inputs == nil || len(inputs) == 0 { 37 | return nil, errors.New("Miss input!") 38 | } 39 | 40 | for _, in := range inputs { 41 | input, err := NewTxIn(in.TxID, in.Vout) 42 | if err != nil { 43 | return nil, err 44 | } 45 | tx.Vins = append(tx.Vins, input) 46 | } 47 | 48 | toBytes, err := addressDecode(to) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | tx.To = toBytes 54 | if amount == 0 { 55 | return nil, errors.New("Invalid amount!") 56 | } 57 | tx.Amount = uint64ToLittleEndianBytes(amount) 58 | if fee == 0 { 59 | return nil, errors.New("Invalid fee!") 60 | } 61 | tx.Fee = uint64ToLittleEndianBytes(fee) 62 | if data == "" { 63 | tx.Data = []byte{0x00} 64 | } else { 65 | tx.Data = append([]byte{byte(len(data))}, []byte(data)...) 66 | } 67 | 68 | return &tx, nil 69 | } 70 | 71 | func (tx TxStruct) ToBytes() []byte { 72 | ret := make([]byte, 0) 73 | 74 | ret = append(ret, tx.Version...) 75 | ret = append(ret, tx.Type...) 76 | ret = append(ret, tx.Time...) 77 | ret = append(ret, tx.LockUntil...) 78 | ret = append(ret, tx.Anchor...) 79 | ret = append(ret, byte(len(tx.Vins))) 80 | for _, in := range tx.Vins { 81 | ret = append(ret, in.ToBytes()...) 82 | } 83 | ret = append(ret, tx.To...) 84 | ret = append(ret, tx.Amount...) 85 | ret = append(ret, tx.Fee...) 86 | ret = append(ret, tx.Data...) 87 | 88 | return ret 89 | } 90 | 91 | func (tx TxStruct) GetHash() []byte { 92 | return owcrypt.Hash(tx.ToBytes(), 32, owcrypt.HASH_ALG_BLAKE2B) 93 | } -------------------------------------------------------------------------------- /bigbangTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package bigbangTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | //reverseBytes endian reverse 10 | func reverseBytes(s []byte) []byte { 11 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 12 | s[i], s[j] = s[j], s[i] 13 | } 14 | return s 15 | } 16 | 17 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 18 | func reverseHexToBytes(hexVar string) ([]byte, error) { 19 | if len(hexVar)%2 == 1 { 20 | return nil, errors.New("Invalid TxHash!") 21 | } 22 | ret, err := hex.DecodeString(hexVar) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return reverseBytes(ret), nil 27 | } 28 | 29 | //reverseBytesToHex change the endian of the input byte array then encode it to hex string 30 | func reverseBytesToHex(bytesVar []byte) string { 31 | return hex.EncodeToString(reverseBytes(bytesVar)) 32 | } 33 | 34 | 35 | 36 | //uint16ToLittleEndianBytes 37 | func uint16ToLittleEndianBytes(data uint16) []byte { 38 | tmp := [2]byte{} 39 | binary.LittleEndian.PutUint16(tmp[:], data) 40 | return tmp[:] 41 | } 42 | 43 | //littleEndianBytesToUint16 44 | func littleEndianBytesToUint16(data []byte) uint16 { 45 | return binary.LittleEndian.Uint16(data) 46 | } 47 | 48 | //uint32ToLittleEndianBytes 49 | func uint32ToLittleEndianBytes(data uint32) []byte { 50 | tmp := [4]byte{} 51 | binary.LittleEndian.PutUint32(tmp[:], data) 52 | return tmp[:] 53 | } 54 | 55 | //uint32ToBigEndianBytes 56 | func uint32ToBigEndianBytes(data uint32) []byte { 57 | tmp := [4]byte{} 58 | binary.BigEndian.PutUint32(tmp[:], data) 59 | return tmp[:] 60 | } 61 | 62 | //littleEndianBytesToUint32 63 | func littleEndianBytesToUint32(data []byte) uint32 { 64 | return binary.LittleEndian.Uint32(data) 65 | } 66 | 67 | //uint64ToLittleEndianBytes 68 | func uint64ToLittleEndianBytes(data uint64) []byte { 69 | tmp := [8]byte{} 70 | binary.LittleEndian.PutUint64(tmp[:], data) 71 | return tmp[:] 72 | } 73 | 74 | //littleEndianBytesToUint64 75 | func littleEndianBytesToUint64(data []byte) uint64 { 76 | return binary.LittleEndian.Uint64(data) 77 | } 78 | -------------------------------------------------------------------------------- /binancechainTransaction/msg-send.go: -------------------------------------------------------------------------------- 1 | package binancechainTransaction 2 | 3 | import ( 4 | "github.com/binance-chain/go-sdk/common/bech32" 5 | ctypes "github.com/binance-chain/go-sdk/common/types" 6 | "github.com/binance-chain/go-sdk/types/msg" 7 | "github.com/pkg/errors" 8 | ) 9 | 10 | func CreateSendMsg(fromAddress, toAddress, denom string, amount int64) (*msg.SendMsg, error) { 11 | prefix, from, err := bech32.DecodeAndConvert(fromAddress) 12 | if err != nil { 13 | return nil, err 14 | } 15 | 16 | if prefix != Bech32Prefix { 17 | return nil, errors.New("Invalid address!") 18 | } 19 | 20 | prefix, to, err := bech32.DecodeAndConvert(toAddress) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | if prefix != Bech32Prefix { 26 | return nil, errors.New("Invalid address!") 27 | } 28 | 29 | coin := ctypes.Coin{ 30 | Denom:denom, 31 | Amount:amount, 32 | } 33 | 34 | transfer := msg.Transfer{ 35 | ToAddr:to, 36 | Coins:ctypes.Coins{coin}, 37 | } 38 | 39 | sendMsg := msg.CreateSendMsg(from, ctypes.Coins{coin},[]msg.Transfer{transfer}) 40 | 41 | return &sendMsg, nil 42 | } -------------------------------------------------------------------------------- /binancechainTransaction/profile.go: -------------------------------------------------------------------------------- 1 | package binancechainTransaction 2 | 3 | const ( 4 | Bech32Prefix = "bnb" 5 | ChainID = "Binance-Chain-Tigris" 6 | ) -------------------------------------------------------------------------------- /binancechainTransaction/stdsign.go: -------------------------------------------------------------------------------- 1 | package binancechainTransaction 2 | 3 | -------------------------------------------------------------------------------- /binancechainTransaction/utils.go: -------------------------------------------------------------------------------- 1 | package binancechainTransaction 2 | 3 | import ( 4 | "github.com/tendermint/tendermint/crypto" 5 | "github.com/tendermint/tendermint/crypto/secp256k1" 6 | "math/big" 7 | ) 8 | 9 | var ( 10 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 11 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 12 | ) 13 | 14 | func serilizeS(sig []byte) []byte { 15 | s := sig[32:] 16 | numS := new(big.Int).SetBytes(s) 17 | numHalfOrder := new(big.Int).SetBytes(HalfCurveOrder) 18 | if numS.Cmp(numHalfOrder) > 0 { 19 | numOrder := new(big.Int).SetBytes(CurveOrder) 20 | numS.Sub(numOrder, numS) 21 | 22 | s = numS.Bytes() 23 | if len(s) < 32 { 24 | for i := 0; i < 32-len(s); i++ { 25 | s = append([]byte{0x00}, s...) 26 | } 27 | } 28 | return append(sig[:32], s...) 29 | } 30 | return sig 31 | } 32 | 33 | func NewPubkey(pubkey []byte) crypto.PubKey { 34 | var pubkeyBytes secp256k1.PubKeySecp256k1 35 | copy(pubkeyBytes[:], pubkey) 36 | return pubkeyBytes 37 | } -------------------------------------------------------------------------------- /bitcoincashTransaction/txOmni.go: -------------------------------------------------------------------------------- 1 | package bitcoincashTransaction 2 | 3 | type OmniStruct struct { 4 | TxType int 5 | PropertyId uint32 6 | Amount uint64 7 | Ecosystem byte 8 | Memo string 9 | Address string 10 | } 11 | 12 | func (os OmniStruct) getPayload() []byte { 13 | payload := []byte{} 14 | switch os.TxType { 15 | case SimpleSend: 16 | payload = createPayloadSimpleSend(os.PropertyId, os.Amount) 17 | break 18 | case SendAll: 19 | payload = createPayloadSendAll(os.Ecosystem) 20 | break 21 | case DExAccept: 22 | payload = createPayloadDExAccept(os.PropertyId, os.Amount) 23 | break 24 | case MetaDExCancelEcosystem: 25 | payload = createPayloadMetaDExCancelEcosystem(os.Ecosystem) 26 | break 27 | case CloseCrowdsale: 28 | payload = createPayloadCloseCrowdsale(os.PropertyId) 29 | break 30 | case Grant: 31 | payload = createPayloadGrant(os.PropertyId, os.Amount, os.Memo) 32 | break 33 | case Revoke: 34 | payload = createPayloadRevoke(os.PropertyId, os.Amount, os.Memo) 35 | break 36 | case ChangeIssuer: 37 | payload = createPayloadChangeIssuer(os.PropertyId) 38 | break 39 | case EnableFreezing: 40 | payload = createPayloadEnableFreezing(os.PropertyId) 41 | break 42 | case DisableFreezing: 43 | payload = createPayloadDisableFreezing(os.PropertyId) 44 | break 45 | case FreezeTokens: 46 | payload = createPayloadFreezeTokens(os.PropertyId, os.Amount, os.Address) 47 | break 48 | case UnfreezeTokens: 49 | payload = createPayloadUnfreezeTokens(os.PropertyId, os.Amount, os.Address) 50 | break 51 | default: 52 | return nil 53 | } 54 | 55 | payload = append(OmniPrefix[:], payload...) 56 | payload = append([]byte{OpReturn, byte(len(payload))}, payload...) 57 | return payload 58 | } 59 | 60 | -------------------------------------------------------------------------------- /bitcoinsvTransaction/txOmni.go: -------------------------------------------------------------------------------- 1 | package bitcoinsvTransaction 2 | 3 | type OmniStruct struct { 4 | TxType int 5 | PropertyId uint32 6 | Amount uint64 7 | Ecosystem byte 8 | Memo string 9 | Address string 10 | } 11 | 12 | func (os OmniStruct) getPayload() []byte { 13 | payload := []byte{} 14 | switch os.TxType { 15 | case SimpleSend: 16 | payload = createPayloadSimpleSend(os.PropertyId, os.Amount) 17 | break 18 | case SendAll: 19 | payload = createPayloadSendAll(os.Ecosystem) 20 | break 21 | case DExAccept: 22 | payload = createPayloadDExAccept(os.PropertyId, os.Amount) 23 | break 24 | case MetaDExCancelEcosystem: 25 | payload = createPayloadMetaDExCancelEcosystem(os.Ecosystem) 26 | break 27 | case CloseCrowdsale: 28 | payload = createPayloadCloseCrowdsale(os.PropertyId) 29 | break 30 | case Grant: 31 | payload = createPayloadGrant(os.PropertyId, os.Amount, os.Memo) 32 | break 33 | case Revoke: 34 | payload = createPayloadRevoke(os.PropertyId, os.Amount, os.Memo) 35 | break 36 | case ChangeIssuer: 37 | payload = createPayloadChangeIssuer(os.PropertyId) 38 | break 39 | case EnableFreezing: 40 | payload = createPayloadEnableFreezing(os.PropertyId) 41 | break 42 | case DisableFreezing: 43 | payload = createPayloadDisableFreezing(os.PropertyId) 44 | break 45 | case FreezeTokens: 46 | payload = createPayloadFreezeTokens(os.PropertyId, os.Amount, os.Address) 47 | break 48 | case UnfreezeTokens: 49 | payload = createPayloadUnfreezeTokens(os.PropertyId, os.Amount, os.Address) 50 | break 51 | default: 52 | return nil 53 | } 54 | 55 | payload = append(OmniPrefix[:], payload...) 56 | payload = append([]byte{OpReturn, byte(len(payload))}, payload...) 57 | return payload 58 | } 59 | 60 | -------------------------------------------------------------------------------- /btcLikeTxDriver/btc.go: -------------------------------------------------------------------------------- 1 | package btcLikeTxDriver 2 | 3 | const ( 4 | P2PKHPrefix = byte(0x6F) 5 | P2SHPrefix = byte(0xC4) 6 | Bech32Prefix = "tb1" 7 | ) 8 | 9 | const ( 10 | SequenceFinal = uint32(0xFFFFFFFF) 11 | SequenceMaxBip125RBF = uint32(0xFFFFFFFD) 12 | ) 13 | 14 | var ( 15 | SegWitSymbol = byte(0) 16 | SegWitVersion = byte(1) 17 | SigHashAll = byte(1) 18 | ) 19 | 20 | var ( 21 | OpCodeHash160 = byte(0xA9) 22 | OpCodeEqual = byte(0x87) 23 | OpCodeEqualVerify = byte(0x88) 24 | OpCodeCheckSig = byte(0xAC) 25 | OpCodeDup = byte(0x76) 26 | OpCode_1 = byte(0x51) 27 | OpCheckMultiSig = byte(0xAE) 28 | ) 29 | 30 | var ( 31 | MaxScriptElementSize = 520 32 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 33 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 34 | ) 35 | -------------------------------------------------------------------------------- /btcLikeTxDriver/multiSig.go: -------------------------------------------------------------------------------- 1 | package btcLikeTxDriver 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | 7 | "github.com/blocktree/go-owcrypt" 8 | ) 9 | 10 | func CreateMultiSig(required byte, pubkeys [][]byte) (string, string, error) { 11 | if required < 1 { 12 | return "", "", errors.New("A multisignature address must require at least one key to redeem!") 13 | } 14 | if required > byte(len(pubkeys)) { 15 | return "", "", errors.New("Not enough keys supplied for a multisignature address to redeem!") 16 | } 17 | if len(pubkeys) > 16 { 18 | return "", "", errors.New("Number of keys involved in the multisignature address creation is too big!") 19 | } 20 | 21 | redeem := []byte{} 22 | 23 | redeem = append(redeem, OpCode_1+required-1) 24 | 25 | for _, k := range pubkeys { 26 | if len(k) != 33 && len(k) != 65 { 27 | return "", "", errors.New("Invalid pubkey data for multisignature address!") 28 | } 29 | redeem = append(redeem, byte(len(k))) 30 | redeem = append(redeem, k...) 31 | } 32 | 33 | redeem = append(redeem, OpCode_1+byte(len(pubkeys))-1) 34 | 35 | redeem = append(redeem, OpCheckMultiSig) 36 | 37 | if len(redeem) > MaxScriptElementSize { 38 | return "", "", errors.New("Redeem script exceeds size limit!") 39 | } 40 | 41 | redeemHash := owcrypt.Hash(redeem, 0, owcrypt.HASH_ALG_SHA256) 42 | redeemHash = append([]byte{0x00, 0x20}, redeemHash...) 43 | redeemHash = owcrypt.Hash(redeemHash, 0, owcrypt.HASH_ALG_HASH160) 44 | 45 | return EncodeCheck(P2SHPrefix, redeemHash), hex.EncodeToString(redeem), nil 46 | } 47 | 48 | func (t Transaction) isMultiSig() bool { 49 | if len(t.Vins) != 1 { 50 | return false 51 | } 52 | if t.Vins[0].ScriptPubkeySignature == nil { 53 | return false 54 | } 55 | if t.Vins[0].ScriptPubkeySignature[len(t.Vins[0].ScriptPubkeySignature)-1] != OpCheckMultiSig { 56 | return false 57 | } 58 | return true 59 | } 60 | 61 | func isMultiSig(lockScript, redeemScript string) bool { 62 | if len(lockScript) != 0x17*2 { 63 | return false 64 | } 65 | 66 | lockBytes, err := hex.DecodeString(lockScript) 67 | if err != nil { 68 | return false 69 | } 70 | if !(lockBytes[0] == OpCodeHash160 && lockBytes[1] == 0x14 || lockBytes[22] == OpCodeEqual) { 71 | return false 72 | } 73 | 74 | redeemBytes, err := hex.DecodeString(redeemScript) 75 | if err != nil { 76 | return false 77 | } 78 | 79 | if len(redeemBytes) == 0 || redeemBytes[len(redeemBytes)-1] != OpCheckMultiSig { 80 | return false 81 | } 82 | return true 83 | 84 | } 85 | 86 | func calcRedeemHash(redeem []byte) []byte { 87 | redeemHash := owcrypt.Hash(redeem, 0, owcrypt.HASH_ALG_SHA256) 88 | 89 | return append([]byte{0x22, 0x00, 0x20}, redeemHash...) 90 | } 91 | -------------------------------------------------------------------------------- /btcLikeTxDriver/txIn.go: -------------------------------------------------------------------------------- 1 | package btcLikeTxDriver 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | ) 7 | 8 | type TxIn struct { 9 | TxID []byte 10 | Vout []byte 11 | ScriptPubkeySignature []byte 12 | Sequence []byte 13 | } 14 | 15 | func (in TxIn) GetTxID() string { 16 | return reverseBytesToHex(in.TxID) 17 | } 18 | 19 | func (in TxIn) GetVout() uint32 { 20 | return littleEndianBytesToUint32(in.Vout) 21 | } 22 | 23 | func (in TxIn) GetScriptPubkey() string { 24 | return hex.EncodeToString(in.ScriptPubkeySignature) 25 | } 26 | 27 | func (in TxIn) GetSequence() uint32 { 28 | return littleEndianBytesToUint32(in.Sequence) 29 | } 30 | 31 | func newTxInForEmptyTrans(vin []Vin) ([]TxIn, error) { 32 | var ret []TxIn 33 | 34 | for _, v := range vin { 35 | txid, err := reverseHexToBytes(v.TxID) 36 | if err != nil || len(txid) != 32 { 37 | return nil, errors.New("Invalid previous txid!") 38 | } 39 | vout := uint32ToLittleEndianBytes(v.Vout) 40 | 41 | ret = append(ret, TxIn{txid[:], vout, nil, nil}) 42 | } 43 | return ret, nil 44 | } 45 | 46 | func (vin *TxIn) setSequence(lockTime uint32, replaceable bool) { 47 | if replaceable { 48 | vin.Sequence = uint32ToLittleEndianBytes(SequenceMaxBip125RBF) 49 | } else if lockTime != 0 { 50 | vin.Sequence = uint32ToLittleEndianBytes(SequenceFinal - 1) 51 | } else { 52 | vin.Sequence = uint32ToLittleEndianBytes(SequenceFinal) 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /btcLikeTxDriver/txOut.go: -------------------------------------------------------------------------------- 1 | package btcLikeTxDriver 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | type TxOut struct { 9 | amount []byte 10 | lockScript []byte 11 | } 12 | 13 | func newTxOutForEmptyTrans(vout []Vout) ([]TxOut, error) { 14 | var ret []TxOut 15 | 16 | for _, v := range vout { 17 | amount := uint64ToLittleEndianBytes(v.Amount) 18 | 19 | if strings.Index(v.Address, Bech32Prefix) == 0 { 20 | redeem, err := Bech32Decode(v.Address) 21 | if err != nil { 22 | return nil, errors.New("Invalid bech32 type address!") 23 | } 24 | 25 | redeem = append([]byte{byte(len(redeem))}, redeem...) 26 | redeem = append([]byte{0x00}, redeem...) 27 | 28 | ret = append(ret, TxOut{amount, redeem}) 29 | } 30 | 31 | prefix, hash, err := DecodeCheck(v.Address) 32 | if err != nil { 33 | return nil, errors.New("Invalid address to send!") 34 | } 35 | 36 | if len(hash) != 0x14 { 37 | return nil, errors.New("Invalid address to send!") 38 | } 39 | 40 | hash = append([]byte{byte(len(hash))}, hash...) 41 | hash = append([]byte{OpCodeHash160}, hash...) 42 | if prefix == P2PKHPrefix { 43 | hash = append(hash, OpCodeEqualVerify, OpCodeCheckSig) 44 | hash = append([]byte{OpCodeDup}, hash...) 45 | } else if prefix == P2SHPrefix { 46 | hash = append(hash, OpCodeEqual) 47 | } else { 48 | return nil, errors.New("Invalid address to send!") 49 | } 50 | 51 | ret = append(ret, TxOut{amount, hash}) 52 | } 53 | return ret, nil 54 | } 55 | -------------------------------------------------------------------------------- /btcLikeTxDriver/utils.go: -------------------------------------------------------------------------------- 1 | package btcLikeTxDriver 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | //reverseBytes endian reverse 10 | func reverseBytes(s []byte) []byte { 11 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 12 | s[i], s[j] = s[j], s[i] 13 | } 14 | return s 15 | } 16 | 17 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 18 | func reverseHexToBytes(hexVar string) ([]byte, error) { 19 | if len(hexVar)%2 == 1 { 20 | return nil, errors.New("Invalid TxHash!") 21 | } 22 | ret, err := hex.DecodeString(hexVar) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return reverseBytes(ret), nil 27 | } 28 | 29 | //reverseBytesToHex change the endian of the input byte array then encode it to hex string 30 | func reverseBytesToHex(bytesVar []byte) string { 31 | return hex.EncodeToString(reverseBytes(bytesVar)) 32 | } 33 | 34 | //uint32ToLittleEndianBytes 35 | func uint32ToLittleEndianBytes(data uint32) []byte { 36 | tmp := [4]byte{} 37 | binary.LittleEndian.PutUint32(tmp[:], data) 38 | return tmp[:] 39 | } 40 | 41 | //littleEndianBytesToUint32 42 | func littleEndianBytesToUint32(data []byte) uint32 { 43 | return binary.LittleEndian.Uint32(data) 44 | } 45 | 46 | //uint64ToLittleEndianBytes 47 | func uint64ToLittleEndianBytes(data uint64) []byte { 48 | tmp := [8]byte{} 49 | binary.LittleEndian.PutUint64(tmp[:], data) 50 | return tmp[:] 51 | } 52 | 53 | //littleEndianBytesToUint64 54 | func littleEndianBytesToUint64(data []byte) uint64 { 55 | return binary.LittleEndian.Uint64(data) 56 | } 57 | -------------------------------------------------------------------------------- /btcLikeTxDriver/witness.go: -------------------------------------------------------------------------------- 1 | package btcLikeTxDriver 2 | 3 | import "errors" 4 | 5 | type TxWitness struct { 6 | Signature []byte 7 | Pubkey []byte 8 | } 9 | 10 | func (w TxWitness) encodeToScript(sigType byte) []byte { 11 | r := w.Signature[:32] 12 | s := w.Signature[32:] 13 | 14 | if r[0]&0x80 == 0x80 { 15 | r = append([]byte{0x00}, r...) 16 | } 17 | if s[0]&0x80 == 0x80 { 18 | s = append([]byte{0}, s...) 19 | } 20 | 21 | r = append([]byte{byte(len(r))}, r...) 22 | r = append([]byte{0x02}, r...) 23 | s = append([]byte{byte(len(s))}, s...) 24 | s = append([]byte{0x02}, s...) 25 | 26 | rs := append(r, s...) 27 | rs = append([]byte{byte(len(rs))}, rs...) 28 | rs = append(rs, sigType) 29 | rs = append([]byte{0x30}, rs...) 30 | rs = append([]byte{byte(len(rs))}, rs...) 31 | 32 | pub := append([]byte{byte(len(w.Pubkey))}, w.Pubkey...) 33 | 34 | return append(rs, pub...) 35 | } 36 | 37 | func decodeFromSegwitBytes(script []byte) (*TxWitness, error) { 38 | var ret TxWitness 39 | index := 0 40 | sigLen := script[index] 41 | index++ 42 | 43 | if script[index] != 0x30 { 44 | return nil, errors.New("Invalid signature data!") 45 | } 46 | index++ 47 | 48 | rsLen := script[index] 49 | index++ 50 | 51 | if script[index] != 0x02 { 52 | return nil, errors.New("Invalid signature data!") 53 | } 54 | index++ 55 | 56 | rLen := script[index] 57 | index++ 58 | 59 | if rLen == 0x21 { 60 | if script[index] != 0x00 && (script[index+1]&0x80 != 0x80) { 61 | return nil, errors.New("Invalid signature data!") 62 | } 63 | index++ 64 | } 65 | 66 | ret.Signature = script[index : index+32] 67 | index += 32 68 | 69 | if script[index] != 0x02 { 70 | return nil, errors.New("Invalid signature data!") 71 | } 72 | index++ 73 | 74 | sLen := script[index] 75 | index++ 76 | 77 | if sLen == 0x21 { 78 | if script[index] != 0x00 && (script[index+1]&0x80 != 0x80) { 79 | return nil, errors.New("Invalid signature data!") 80 | } 81 | index++ 82 | } 83 | 84 | ret.Signature = append(ret.Signature, script[index:index+32]...) 85 | index += 32 86 | 87 | if script[index] != SigHashAll { 88 | return nil, errors.New("Only sigAll supported!") 89 | } 90 | index++ 91 | 92 | pubLen := script[index] 93 | index++ 94 | if pubLen != 0x21 { 95 | return nil, errors.New("Only compressed pubkey is supported!") 96 | } 97 | 98 | ret.Pubkey = script[index : index+33] 99 | index += 33 100 | 101 | if (rLen+sLen+4 != rsLen) || (rsLen+3 != sigLen) || (sigLen+pubLen+2 != byte(len(script))) { 102 | return nil, errors.New("Invalid transaction data!") 103 | } 104 | 105 | if index != len(script) { 106 | return nil, errors.New("Invalid transaction data!") 107 | } 108 | return &ret, nil 109 | } 110 | -------------------------------------------------------------------------------- /btcTransaction/txOut.go: -------------------------------------------------------------------------------- 1 | package btcTransaction 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | type TxOut struct { 9 | amount []byte 10 | lockScript []byte 11 | } 12 | 13 | func newTxOutForEmptyTrans(vout []Vout, addressPrefix AddressPrefix) ([]TxOut, error) { 14 | if vout == nil || len(vout) == 0 { 15 | return nil, errors.New("No address to send when create an empty transaction!") 16 | } 17 | var ret []TxOut 18 | var prefixStr string 19 | var p2pkhPrefixByte []byte 20 | var p2wpkhPrefixByte []byte 21 | var p2shPrefixBytes []byte 22 | prefixStr = addressPrefix.Bech32Prefix 23 | p2pkhPrefixByte = addressPrefix.P2PKHPrefix 24 | p2wpkhPrefixByte = addressPrefix.P2WPKHPrefix 25 | p2shPrefixBytes = addressPrefix.P2SHPrefix 26 | 27 | for _, v := range vout { 28 | amount := uint64ToLittleEndianBytes(v.Amount) 29 | 30 | if strings.Index(v.Address, prefixStr) == 0 { 31 | redeem, err := Bech32Decode(v.Address) 32 | if err != nil { 33 | return nil, errors.New("Invalid bech32 type address!") 34 | } 35 | 36 | redeem = append([]byte{byte(len(redeem))}, redeem...) 37 | redeem = append([]byte{0x00}, redeem...) 38 | 39 | ret = append(ret, TxOut{amount, redeem}) 40 | 41 | continue 42 | } 43 | 44 | prefix, hash, err := DecodeCheck(v.Address) 45 | if err != nil { 46 | return nil, errors.New("Invalid address to send!") 47 | } 48 | 49 | if len(hash) != 0x14 { 50 | return nil, errors.New("Invalid address to send!") 51 | } 52 | 53 | hash = append([]byte{byte(len(hash))}, hash...) 54 | hash = append([]byte{OpCodeHash160}, hash...) 55 | if byteArrayCompare(prefix, p2pkhPrefixByte) { 56 | hash = append(hash, OpCodeEqualVerify, OpCodeCheckSig) 57 | hash = append([]byte{OpCodeDup}, hash...) 58 | } else if byteArrayCompare(prefix, p2wpkhPrefixByte) || byteArrayCompare(prefix, p2shPrefixBytes) { 59 | hash = append(hash, OpCodeEqual) 60 | } else { 61 | return nil, errors.New("Invalid address to send!") 62 | } 63 | 64 | ret = append(ret, TxOut{amount, hash}) 65 | } 66 | return ret, nil 67 | } 68 | 69 | func (out TxOut) toBytes() ([]byte, error) { 70 | if out.amount == nil || len(out.amount) != 8 { 71 | return nil, errors.New("Invalid amount for a transaction output!") 72 | } 73 | if out.lockScript == nil || len(out.lockScript) == 0 { 74 | return nil, errors.New("Invalid lock script for a transaction output!") 75 | } 76 | 77 | ret := []byte{} 78 | ret = append(ret, out.amount...) 79 | ret = append(ret, byte(len(out.lockScript))) 80 | ret = append(ret, out.lockScript...) 81 | 82 | return ret, nil 83 | } 84 | -------------------------------------------------------------------------------- /btcTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package btcTransaction 2 | 3 | type AddressPrefix struct { 4 | P2PKHPrefix []byte 5 | P2WPKHPrefix []byte 6 | P2SHPrefix []byte 7 | Bech32Prefix string 8 | } 9 | 10 | var ( 11 | BTCMainnetAddressPrefix = AddressPrefix{[]byte{0x00}, []byte{0x05}, nil, "bc"} 12 | BTCTestnetAddressPrefix = AddressPrefix{[]byte{0x6F}, []byte{0xC4}, nil, "tb"} 13 | BCHMainnetAddressPrefix = AddressPrefix{[]byte{0x00}, []byte{0x05}, nil, "bc"} 14 | BCHTestnetAddressPrefix = AddressPrefix{[]byte{0x6F}, []byte{0xC4}, nil, "tb"} 15 | LTCMainnetAddressPrefix = AddressPrefix{[]byte{0x30}, []byte{0x05}, []byte{0x32}, "ltc"} 16 | LTCTestnetAddressPrefix = AddressPrefix{[]byte{0x6F}, []byte{0xC4}, []byte{0x3A}, "tltc"} 17 | ZECMainnetAddressPrefix = AddressPrefix{[]byte{0x1C, 0xB8}, []byte{0x1C, 0xBD}, nil, ""} 18 | ZECTestnetAddressPrefix = AddressPrefix{[]byte{0x1D, 0x25}, []byte{0x1C, 0xBA}, nil, ""} 19 | ) 20 | 21 | const ( 22 | DefaultTxVersion = uint32(2) 23 | DefaultHashType = uint32(1) 24 | MaxScriptElementSize = 520 25 | ) 26 | 27 | const ( 28 | SequenceFinal = uint32(0xFFFFFFFF) 29 | SequenceMaxBip125RBF = uint32(0xFFFFFFFD) 30 | ) 31 | 32 | const ( 33 | SegWitSymbol = byte(0) 34 | SegWitVersion = byte(1) 35 | SigHashAll = byte(1) 36 | ) 37 | 38 | const ( 39 | OpCodeHash160 = byte(0xA9) 40 | OpCodeEqual = byte(0x87) 41 | OpCodeEqualVerify = byte(0x88) 42 | OpCodeCheckSig = byte(0xAC) 43 | OpCodeDup = byte(0x76) 44 | OpCode_1 = byte(0x51) 45 | OpCheckMultiSig = byte(0xAE) 46 | OpPushData1 = byte(0x4C) 47 | OpPushData2 = byte(0x4D) 48 | OpPushData3 = byte(0x4E) 49 | ) 50 | 51 | var ( 52 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 53 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 54 | ) 55 | -------------------------------------------------------------------------------- /btcTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package btcTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | func byteArrayCompare(a, b []byte) bool { 10 | if len(a) != len(b) { 11 | return false 12 | } 13 | for index := 0; index < len(a); index++ { 14 | if a[index] != b[index] { 15 | return false 16 | } 17 | } 18 | return true 19 | } 20 | 21 | //reverseBytes endian reverse 22 | func reverseBytes(s []byte) []byte { 23 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 24 | s[i], s[j] = s[j], s[i] 25 | } 26 | return s 27 | } 28 | 29 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 30 | func reverseHexToBytes(hexVar string) ([]byte, error) { 31 | if len(hexVar)%2 == 1 { 32 | return nil, errors.New("Invalid TxHash!") 33 | } 34 | ret, err := hex.DecodeString(hexVar) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return reverseBytes(ret), nil 39 | } 40 | 41 | //reverseBytesToHex change the endian of the input byte array then encode it to hex string 42 | func reverseBytesToHex(bytesVar []byte) string { 43 | return hex.EncodeToString(reverseBytes(bytesVar)) 44 | } 45 | 46 | //uint16ToLittleEndianBytes 47 | func uint16ToLittleEndianBytes(data uint16) []byte { 48 | tmp := [2]byte{} 49 | binary.LittleEndian.PutUint16(tmp[:], data) 50 | return tmp[:] 51 | } 52 | 53 | //littleEndianBytesToUint16 54 | func littleEndianBytesToUint16(data []byte) uint16 { 55 | return binary.LittleEndian.Uint16(data) 56 | } 57 | 58 | //uint32ToLittleEndianBytes 59 | func uint32ToLittleEndianBytes(data uint32) []byte { 60 | tmp := [4]byte{} 61 | binary.LittleEndian.PutUint32(tmp[:], data) 62 | return tmp[:] 63 | } 64 | 65 | //littleEndianBytesToUint32 66 | func littleEndianBytesToUint32(data []byte) uint32 { 67 | return binary.LittleEndian.Uint32(data) 68 | } 69 | 70 | //uint64ToLittleEndianBytes 71 | func uint64ToLittleEndianBytes(data uint64) []byte { 72 | tmp := [8]byte{} 73 | binary.LittleEndian.PutUint64(tmp[:], data) 74 | return tmp[:] 75 | } 76 | 77 | //littleEndianBytesToUint64 78 | func littleEndianBytesToUint64(data []byte) uint64 { 79 | return binary.LittleEndian.Uint64(data) 80 | } 81 | -------------------------------------------------------------------------------- /bytomTransaction/README.md: -------------------------------------------------------------------------------- 1 | # Bytom Transaction Driver 2 | ``` 3 | transaction_test.go测试案例说明 4 | Test_case1 : 花费单个P2WPKH-UTXO 5 | Test_case2 : 花费多个P2WPKH-UTXO 6 | Test_case3 : 多签地址构建验证 7 | Test_case4 : 花费多签掉UTXO 8 | ``` 9 | ## 当前支持 10 | ``` 11 | 交易单构建 12 | 交易单签名 13 | 交易单合并 14 | 交易单验签 15 | 16 | P2WPKH 17 | Multisig 18 | ``` 19 | ## TODO 20 | ``` 21 | 智能合约 22 | ``` 23 | -------------------------------------------------------------------------------- /bytomTransaction/sigPub.go: -------------------------------------------------------------------------------- 1 | package bytomTransaction 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/blocktree/go-owcrypt" 7 | ) 8 | 9 | type SigPub struct { 10 | Signature []byte 11 | Pubkey []byte 12 | } 13 | 14 | func (sp *SigPub) toBytes() ([]byte, error) { 15 | if sp == nil || sp.Signature == nil || sp.Pubkey == nil { 16 | return nil, errors.New("Miss signature or public key data!") 17 | } 18 | 19 | if len(sp.Signature) != 64 || len(sp.Pubkey) != 32 { 20 | return nil, errors.New("Invalid signature or public key data!") 21 | } 22 | 23 | var ret []byte 24 | 25 | ret = append(ret, 0x63, 0x02, 0x40) 26 | ret = append(ret, sp.Signature...) 27 | ret = append(ret, 0x20) 28 | ret = append(ret, sp.Pubkey...) 29 | 30 | return ret, nil 31 | } 32 | 33 | func calcSignaturePubkey(hash, prikey []byte) (*SigPub, error) { 34 | var sp SigPub 35 | if hash == nil || len(hash) != 32 || prikey == nil || len(prikey) != 32 { 36 | return nil, errors.New("Miss transaction hash or prikey data!") 37 | } 38 | 39 | sig,_, err := owcrypt.Signature(prikey, nil, hash, owcrypt.ECC_CURVE_ED25519) 40 | 41 | if err != owcrypt.SUCCESS { 42 | return nil, errors.New("sign error!") 43 | } 44 | 45 | sp.Signature = sig 46 | 47 | pub := owcrypt.Point_mulBaseG(prikey, owcrypt.ECC_CURVE_ED25519) 48 | 49 | sp.Pubkey = pub 50 | 51 | return &sp, nil 52 | } 53 | -------------------------------------------------------------------------------- /bytomTransaction/txHash.go: -------------------------------------------------------------------------------- 1 | package bytomTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | 7 | "github.com/blocktree/go-owcrypt" 8 | ) 9 | 10 | type NormalTx struct { 11 | Address string 12 | SigPub SigPub 13 | } 14 | 15 | type MultiTx struct { 16 | Pubkey string 17 | SigPub SigPub 18 | } 19 | 20 | type TxHash struct { 21 | Hash string 22 | NRequired byte 23 | Normal *NormalTx 24 | Multi []MultiTx 25 | } 26 | 27 | func (th TxHash) IsMultiSig() bool { 28 | return th.NRequired != 0 29 | } 30 | 31 | func (th *TxHash) PadMultiSig(signScriptHex string) error { 32 | signScript, err := hex.DecodeString(signScriptHex) 33 | if err != nil { 34 | return errors.New("Invalid signScript hex string!") 35 | } 36 | 37 | multi, required, err := getMultiSigDetail(signScript) 38 | if err != nil { 39 | return err 40 | } 41 | 42 | th.NRequired = required 43 | th.Multi = multi 44 | return nil 45 | } 46 | 47 | func (th TxHash) getMultiSigBytes() ([]byte, []byte, error) { 48 | if !th.IsMultiSig() { 49 | return nil, nil, errors.New("Not a multisig txhash!") 50 | } 51 | 52 | signScript := []byte{} 53 | 54 | signScript = append(signScript, Op_TxSignHash) 55 | 56 | for _, key := range th.Multi { 57 | if key.SigPub.Pubkey == nil || len(key.SigPub.Pubkey) != 0x20 { 58 | return nil, nil, errors.New("Invalid pubkey data for create multisig!") 59 | } 60 | signScript = append(signScript, 0x20) 61 | signScript = append(signScript, key.SigPub.Pubkey...) 62 | } 63 | 64 | signScript = append(signScript, Op_1-1+th.NRequired) 65 | signScript = append(signScript, Op_1-1+byte(len(th.Multi))) 66 | signScript = append(signScript, Op_CheckMultiSig) 67 | 68 | scriptHash := owcrypt.Hash(signScript, 0, owcrypt.HASH_ALG_SHA3_256) 69 | 70 | scriptHash = append([]byte{0x00, 0x20}, scriptHash...) 71 | 72 | sigBytes := []byte{} 73 | 74 | sigBytes = append(sigBytes, 0x01, 0x03) 75 | 76 | count := 0 77 | for _, sp := range th.Multi { 78 | if sp.SigPub.Signature == nil { 79 | continue 80 | } 81 | if len(sp.SigPub.Signature) != 0x40 { 82 | return nil, nil, errors.New("Invalid length of signature!") 83 | } 84 | 85 | sigBytes = append(sigBytes, 0x40) 86 | sigBytes = append(sigBytes, sp.SigPub.Signature...) 87 | count++ 88 | 89 | if byte(count) == th.NRequired { 90 | break 91 | } 92 | } 93 | 94 | if byte(count) < th.NRequired { 95 | return nil, nil, errors.New("Not competely signed!") 96 | } 97 | 98 | sigBytes = append(sigBytes, byte(len(signScript))) 99 | sigBytes = append(sigBytes, signScript...) 100 | 101 | sigBytes = append([]byte{byte(len(sigBytes) - 1)}, sigBytes...) 102 | return sigBytes, scriptHash, nil 103 | } 104 | -------------------------------------------------------------------------------- /bytomTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package bytomTransaction 2 | 3 | const ( 4 | Bech32HRPSegwitMainNet = "bm" 5 | Bech32HRPSegwitTestNet = "tm" 6 | HashPrefixEntry = "entryid:" 7 | HashPrefixOutput = "output1:" 8 | HashPrefixSpend = "spend1:" 9 | HashPrefixMux = "mux1:" 10 | HashPrefixTxheader = "txheader:" 11 | DefaultWitnessVersion = byte(0) 12 | DefaultSerFlags = byte(7) 13 | DefaultTransactionVersion = uint64(1) 14 | DefaultAssetVersion = byte(1) 15 | DefaultOutVersion = byte(1) 16 | DefaultVMVersion = uint64(1) 17 | Op_true = byte(0x51) 18 | Op_1 = byte(0x51) 19 | Op_TxSignHash = byte(0xAE) 20 | Op_CheckMultiSig = byte(0xAD) 21 | 22 | BTMAssetID = "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff" 23 | ) 24 | -------------------------------------------------------------------------------- /bytomTransaction/txUtil.go: -------------------------------------------------------------------------------- 1 | package bytomTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | ) 6 | 7 | func uint64ToUvarint(x uint64) []byte { 8 | ret := [8]byte{} 9 | 10 | len := binary.PutUvarint(ret[:], x) 11 | 12 | if len == 0 { 13 | return nil 14 | } 15 | return ret[:len] 16 | } 17 | 18 | func uvarintToUint64(buf []byte) uint64 { 19 | if buf == nil || len(buf) == 0x00 { 20 | return 0 21 | } 22 | 23 | ret, length := binary.Uvarint(buf) 24 | 25 | if length != len(buf) { 26 | return 0 27 | } 28 | 29 | return ret 30 | } 31 | 32 | //uint64ToLittleEndianBytes 33 | func uint64ToLittleEndianBytes(data uint64) []byte { 34 | tmp := [8]byte{} 35 | binary.LittleEndian.PutUint64(tmp[:], data) 36 | return tmp[:] 37 | } 38 | 39 | //littleEndianBytesToUint64 40 | func littleEndianBytesToUint64(data []byte) uint64 { 41 | return binary.LittleEndian.Uint64(data) 42 | } 43 | -------------------------------------------------------------------------------- /cosmosTransaction/bech32_test.go: -------------------------------------------------------------------------------- 1 | package cosmosTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_ATOMAddress(t *testing.T) { 10 | hash, _ := hex.DecodeString("3335a8768bf87fbd1e554e71a82da2809110e190") 11 | address := Bech32Encode("cosmos", ATOMBech32Alphabet, hash) 12 | 13 | if address != "cosmos1xv66sa5tlplm68j4fec6stdzszg3pcvswag06j" { 14 | t.Error("atom address encode failed!") 15 | } else { 16 | fmt.Println("atom address encode success!") 17 | fmt.Println(address) 18 | } 19 | 20 | check, err := Bech32Decode(address) 21 | 22 | if err != nil { 23 | t.Error("atom address decode failed!") 24 | } else { 25 | for index := 0; index < 20; index++ { 26 | if check[index] != hash[index] { 27 | t.Error("atom address decode failed!") 28 | } 29 | } 30 | fmt.Println("atom address decode success!") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /cosmosTransaction/coin.go: -------------------------------------------------------------------------------- 1 | package cosmosTransaction 2 | 3 | import ( 4 | "math/big" 5 | "strconv" 6 | ) 7 | 8 | type Int struct { 9 | i *big.Int 10 | } 11 | 12 | type Coin struct { 13 | Amount string `json:"amount"` 14 | Denom string `json:"denom"` 15 | } 16 | type Coins []Coin 17 | 18 | func NewInt(n int64) Int { 19 | return Int{big.NewInt(n)} 20 | } 21 | 22 | func NewIntFromBigInt(i *big.Int) Int { 23 | if i.BitLen() > 255 { 24 | panic("NewIntFromBigInt() out of bound") 25 | } 26 | return Int{i} 27 | } 28 | 29 | func lt(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) == -1 } 30 | 31 | func (i Int) LT(i2 Int) bool { 32 | return lt(i.i, i2.i) 33 | } 34 | 35 | func ZeroInt() Int { return Int{big.NewInt(0)} } 36 | 37 | func NewCoin(denom string, amount int64) Coin { 38 | 39 | if amount == 0 { 40 | return Coin{} 41 | } 42 | 43 | return Coin{ 44 | Denom: denom, 45 | Amount: strconv.FormatInt(amount, 10), 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /cosmosTransaction/fee.go: -------------------------------------------------------------------------------- 1 | package cosmosTransaction 2 | 3 | import "strconv" 4 | 5 | type FeeStruct struct { 6 | Amount Coins `json:"amount"` 7 | Gas string `json:"gas"` 8 | } 9 | 10 | func NewStdFee(gas int64, amount Coins) FeeStruct { 11 | 12 | if amount == nil { 13 | return FeeStruct{ 14 | Amount: Coins{}, 15 | Gas: strconv.FormatInt(gas, 10), 16 | } 17 | } 18 | 19 | return FeeStruct{ 20 | Amount: amount, 21 | Gas: strconv.FormatInt(gas, 10), 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /cosmosTransaction/json_test.go: -------------------------------------------------------------------------------- 1 | package cosmosTransaction 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_json(t *testing.T) { 10 | tx := TxStruct{ 11 | AccountNumber: "1858", 12 | ChainID: "gaia-13003", 13 | Fee: FeeStruct{ 14 | Amount: Coins{}, 15 | Gas: "200000", 16 | }, 17 | Memo: "", 18 | Message: []Message{NewMessage("cosmos-sdk/MsgSend", NewMsgSend("cosmos1x9rdj3pgk9l3fvuj0fzxwa38vz276ljcysewnn", "cosmos1xv66sa5tlplm68j4fec6stdzszg3pcvswag06j", Coins{NewCoin("muon", 1000000)}))}, 19 | Sequence: "2", 20 | } 21 | 22 | b, err := json.Marshal(tx) 23 | if err != nil { 24 | fmt.Println(err) 25 | } 26 | fmt.Println(string(b)) 27 | } 28 | -------------------------------------------------------------------------------- /cosmosTransaction/message.go: -------------------------------------------------------------------------------- 1 | package cosmosTransaction 2 | 3 | type MsgSend struct { 4 | Amount Coins `json:"amount"` 5 | FromAddress string `json:"from_address"` 6 | ToAddress string `json:"to_address"` 7 | } 8 | 9 | type Message struct { 10 | Type string `json:"type"` 11 | Value MsgSend `json:"value"` 12 | } 13 | 14 | func NewMsgSend(fromAddr, toAddr string, amount Coins) MsgSend { 15 | return MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amount} 16 | } 17 | 18 | func NewMessage(msgType string, value MsgSend) Message { 19 | return Message{Type: msgType, Value: value} 20 | } 21 | -------------------------------------------------------------------------------- /cosmosTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package cosmosTransaction 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func Test_case1(t *testing.T) { 9 | denom := "muon" 10 | chainID := "gaia-13003" 11 | accountNumber := 1858 12 | memo := "" 13 | sequence := 2 14 | from := "cosmos1x9rdj3pgk9l3fvuj0fzxwa38vz276ljcysewnn" 15 | to := "cosmos1xv66sa5tlplm68j4fec6stdzszg3pcvswag06j" 16 | amount := int64(1000000) 17 | 18 | gas := int64(200000) 19 | feeAmount := int64(0) 20 | 21 | fee := NewStdFee(gas, Coins{NewCoin(denom, feeAmount)}) 22 | 23 | messageType := "cosmos-sdk/MsgSend" 24 | message := []Message{NewMessage(messageType, NewMsgSend(from, to, Coins{NewCoin(denom, amount)}))} 25 | 26 | tx := NewTxStruct(chainID, memo, accountNumber, sequence, &fee, message) 27 | 28 | emptyTrans, hash, err := tx.CreateEmptyTransactionAndHash() 29 | if err != nil { 30 | t.Error("create empty transaction failed!") 31 | } else { 32 | fmt.Println("empty transaction : ", emptyTrans) 33 | fmt.Println("hash : ", hash) 34 | } 35 | 36 | prikey := []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 37 | 38 | sig, err := SignTransactionHash(hash, prikey) 39 | 40 | if err != nil { 41 | t.Error("sign transaction failed!") 42 | } else { 43 | fmt.Println("signature: ", sig) 44 | } 45 | 46 | pubkey := []byte{0x03, 0x72, 0x47, 0x8b, 0xc9, 0x3a, 0xe8, 0x27, 0xa0, 0xd5, 0x8d, 0x5b, 0x1f, 0x31, 0xd1, 0x5d, 0x9d, 0x8c, 0xf2, 0x09, 0xcb, 0x1a, 0xe2, 0x04, 0x8b, 0xae, 0x54, 0x45, 0x73, 0x18, 0x19, 0xea, 0x14} 47 | keyType := "tendermint/PubKeySecp256k1" 48 | mode := "block" 49 | ret, err := tx.CreateJsonForSend(sig, pubkey, keyType, mode) 50 | if err != nil { 51 | t.Error("create json for send failed!") 52 | } else { 53 | fmt.Println("transaction for send: ", ret) 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /cosmosTransaction/txSend.go: -------------------------------------------------------------------------------- 1 | package cosmosTransaction 2 | 3 | import "encoding/base64" 4 | 5 | type Pub struct { 6 | Type string `json:"type"` 7 | Value string `json:"value"` 8 | } 9 | 10 | func NewPub(pubkey []byte, curveType string) Pub { 11 | return Pub{ 12 | Type: curveType, 13 | Value: base64.StdEncoding.EncodeToString(pubkey), 14 | } 15 | } 16 | 17 | type Sig struct { 18 | Signature string `json:"signature"` 19 | Pubkey Pub `json:"pub_key"` 20 | AccountNumber string `json:"account_number"` 21 | Sequence string `json:"sequence"` 22 | } 23 | 24 | func NewSig(signature []byte, accountNumber, sequence string, pubkey Pub) Sig { 25 | return Sig{ 26 | Signature: base64.StdEncoding.EncodeToString(signature), 27 | Pubkey: pubkey, 28 | AccountNumber: accountNumber, 29 | Sequence: sequence, 30 | } 31 | } 32 | 33 | type Tx struct { 34 | Message []Message `json:"msg"` 35 | Fee FeeStruct `json:"fee"` 36 | Memo string `json:"memo"` 37 | Signature []Sig `json:"signatures"` 38 | } 39 | 40 | func NewTx(message []Message, memo string, fee FeeStruct, signature []Sig) Tx { 41 | return Tx{ 42 | Message: message, 43 | Fee: fee, 44 | Memo: memo, 45 | Signature: signature, 46 | } 47 | } 48 | 49 | type TxSend struct { 50 | Tx Tx `json:"tx"` 51 | Mode string `json:"mode"` 52 | } 53 | 54 | func NewTxSend(tx Tx, mode string) TxSend { 55 | return TxSend{ 56 | Tx: tx, 57 | Mode: mode, 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /cosmosTransaction/txStruct.go: -------------------------------------------------------------------------------- 1 | package cosmosTransaction 2 | 3 | import "strconv" 4 | 5 | type TxStruct struct { 6 | AccountNumber string `json:"account_number"` 7 | ChainID string `json:"chain_id"` 8 | Fee FeeStruct `json:"fee"` 9 | Memo string `json:"memo"` 10 | Message []Message `json:"msgs"` 11 | Sequence string `json:"sequence"` 12 | } 13 | 14 | func NewTxStruct(chainID, memo string, accountNumber, sequence int, fee *FeeStruct, message []Message) TxStruct { 15 | if fee.Amount[0].Amount == "" { 16 | fee.Amount = Coins{} 17 | } 18 | return TxStruct{ 19 | AccountNumber: strconv.FormatInt(int64(accountNumber), 10), 20 | ChainID: chainID, 21 | Fee: *fee, 22 | Memo: memo, 23 | Message: message, 24 | Sequence: strconv.FormatInt(int64(sequence), 10), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cosmosTransaction/utils.go: -------------------------------------------------------------------------------- 1 | package cosmosTransaction 2 | -------------------------------------------------------------------------------- /cxcTransaction/txAsset.go: -------------------------------------------------------------------------------- 1 | package cxcTransaction 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | func getTransferPayload(to AssetTransfer) []byte { 9 | ret := []byte(AssetTransferSign) 10 | 11 | sellfirstBytes, err := reverseHexToBytes(to.FirstSellTxID) 12 | if err != nil { 13 | return nil 14 | } 15 | 16 | ret = append(ret, sellfirstBytes...) 17 | ret = append(ret, uint64ToLittleEndianBytes(to.Amount)...) 18 | 19 | return ret 20 | } 21 | 22 | func getTransferAssetWithPayload(tos []AssetTransfer, addressPrefix AddressPrefix) (*[]TxOut, error) { 23 | 24 | var prefixStr string 25 | var p2pkhPrefixByte []byte 26 | var p2wpkhPrefixByte []byte 27 | prefixStr = addressPrefix.Bech32Prefix 28 | p2pkhPrefixByte = addressPrefix.P2PKHPrefix 29 | p2wpkhPrefixByte = addressPrefix.P2WPKHPrefix 30 | 31 | ret := make([]TxOut, 0) 32 | for _, to := range tos { 33 | if strings.Index(to.Address, prefixStr) == 0 { 34 | redeem, err := Bech32Decode(to.Address) 35 | if err != nil { 36 | return nil, errors.New("Invalid bech32 type address!") 37 | } 38 | 39 | redeem = append([]byte{byte(len(redeem))}, redeem...) 40 | redeem = append([]byte{0x00}, redeem...) 41 | payload := getTransferPayload(to) 42 | if payload == nil { 43 | return nil, errors.New("Failed to get asset payload!") 44 | } 45 | redeem = append(redeem, byte(len(payload))) 46 | redeem = append(redeem, payload...) 47 | redeem = append(redeem, OpDrop) 48 | ret = append(ret, TxOut{uint64ToLittleEndianBytes(0), redeem}) 49 | } 50 | 51 | prefix, hash, err := DecodeCheck(to.Address) 52 | if err != nil { 53 | return nil, errors.New("Invalid address to send!") 54 | } 55 | 56 | if len(hash) != 0x14 { 57 | return nil, errors.New("Invalid address to send!") 58 | } 59 | 60 | hash = append([]byte{byte(len(hash))}, hash...) 61 | hash = append([]byte{OpCodeHash160}, hash...) 62 | if byteArrayCompare(prefix, p2pkhPrefixByte) { 63 | hash = append(hash, OpCodeEqualVerify, OpCodeCheckSig) 64 | hash = append([]byte{OpCodeDup}, hash...) 65 | } else if byteArrayCompare(prefix, p2wpkhPrefixByte) { 66 | hash = append(hash, OpCodeEqual) 67 | } else { 68 | return nil, errors.New("Invalid address to send!") 69 | } 70 | 71 | payload := getTransferPayload(to) 72 | if payload == nil { 73 | return nil, errors.New("Failed to get asset payload!") 74 | } 75 | hash = append(hash, byte(len(payload))) 76 | hash = append(hash, payload...) 77 | hash = append(hash, OpDrop) 78 | ret = append(ret, TxOut{uint64ToLittleEndianBytes(0), hash}) 79 | } 80 | 81 | return &ret, nil 82 | } 83 | -------------------------------------------------------------------------------- /cxcTransaction/txOut.go: -------------------------------------------------------------------------------- 1 | package cxcTransaction 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | type TxOut struct { 9 | amount []byte 10 | lockScript []byte 11 | } 12 | 13 | func newTxOutForEmptyTrans(vout []Vout, addressPrefix AddressPrefix) ([]TxOut, error) { 14 | if vout == nil || len(vout) == 0 { 15 | return nil, errors.New("No address to send when create an empty transaction!") 16 | } 17 | var ret []TxOut 18 | var prefixStr string 19 | var p2pkhPrefixByte []byte 20 | var p2wpkhPrefixByte []byte 21 | prefixStr = addressPrefix.Bech32Prefix 22 | p2pkhPrefixByte = addressPrefix.P2PKHPrefix 23 | p2wpkhPrefixByte = addressPrefix.P2WPKHPrefix 24 | 25 | for _, v := range vout { 26 | amount := uint64ToLittleEndianBytes(v.Amount) 27 | 28 | if strings.Index(v.Address, prefixStr) == 0 { 29 | redeem, err := Bech32Decode(v.Address) 30 | if err != nil { 31 | return nil, errors.New("Invalid bech32 type address!") 32 | } 33 | 34 | redeem = append([]byte{byte(len(redeem))}, redeem...) 35 | redeem = append([]byte{0x00}, redeem...) 36 | 37 | ret = append(ret, TxOut{amount, redeem}) 38 | } 39 | 40 | prefix, hash, err := DecodeCheck(v.Address) 41 | if err != nil { 42 | return nil, errors.New("Invalid address to send!") 43 | } 44 | 45 | if len(hash) != 0x14 { 46 | return nil, errors.New("Invalid address to send!") 47 | } 48 | 49 | hash = append([]byte{byte(len(hash))}, hash...) 50 | hash = append([]byte{OpCodeHash160}, hash...) 51 | if byteArrayCompare(prefix, p2pkhPrefixByte) { 52 | hash = append(hash, OpCodeEqualVerify, OpCodeCheckSig) 53 | hash = append([]byte{OpCodeDup}, hash...) 54 | } else if byteArrayCompare(prefix, p2wpkhPrefixByte) { 55 | hash = append(hash, OpCodeEqual) 56 | } else { 57 | return nil, errors.New("Invalid address to send!") 58 | } 59 | 60 | ret = append(ret, TxOut{amount, hash}) 61 | } 62 | return ret, nil 63 | } 64 | 65 | func (out TxOut) toBytes() ([]byte, error) { 66 | if out.amount == nil || len(out.amount) != 8 { 67 | return nil, errors.New("Invalid amount for a transaction output!") 68 | } 69 | if out.lockScript == nil || len(out.lockScript) == 0 { 70 | return nil, errors.New("Invalid lock script for a transaction output!") 71 | } 72 | 73 | ret := []byte{} 74 | ret = append(ret, out.amount...) 75 | ret = append(ret, byte(len(out.lockScript))) 76 | ret = append(ret, out.lockScript...) 77 | 78 | return ret, nil 79 | } 80 | -------------------------------------------------------------------------------- /cxcTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package cxcTransaction 2 | 3 | type AddressPrefix struct { 4 | P2PKHPrefix []byte 5 | P2WPKHPrefix []byte 6 | Bech32Prefix string 7 | } 8 | 9 | var ( 10 | BTCMainnetAddressPrefix = AddressPrefix{[]byte{0x00}, []byte{0x05}, "bc"} 11 | BTCTestnetAddressPrefix = AddressPrefix{[]byte{0x6F}, []byte{0xC4}, "tb"} 12 | BCHMainnetAddressPrefix = AddressPrefix{[]byte{0x00}, []byte{0x05}, "bc"} 13 | BCHTestnetAddressPrefix = AddressPrefix{[]byte{0x6F}, []byte{0xC4}, "tb"} 14 | LTCMainnetAddressPrefix = AddressPrefix{[]byte{0x30}, []byte{0x05}, "ltc"} 15 | LTCTestnetAddressPrefix = AddressPrefix{[]byte{0x6F}, []byte{0xC4}, "tltc"} 16 | ZECMainnetAddressPrefix = AddressPrefix{[]byte{0x1C, 0xB8}, []byte{0x1C, 0xBD}, ""} 17 | ZECTestnetAddressPrefix = AddressPrefix{[]byte{0x1D, 0x25}, []byte{0x1C, 0xBA}, ""} 18 | ) 19 | 20 | const ( 21 | DefaultTxVersion = uint32(1) 22 | DefaultHashType = uint32(1) 23 | MaxScriptElementSize = 520 24 | AssetTransferSign = "spkq" 25 | ) 26 | 27 | const ( 28 | SequenceFinal = uint32(0xFFFFFFFF) 29 | SequenceMaxBip125RBF = uint32(0xFFFFFFFD) 30 | ) 31 | 32 | const ( 33 | SegWitSymbol = byte(0) 34 | SegWitVersion = byte(1) 35 | SigHashAll = byte(1) 36 | ) 37 | 38 | const ( 39 | OpCodeHash160 = byte(0xA9) 40 | OpCodeEqual = byte(0x87) 41 | OpCodeEqualVerify = byte(0x88) 42 | OpCodeCheckSig = byte(0xAC) 43 | OpCodeDup = byte(0x76) 44 | OpCode_1 = byte(0x51) 45 | OpCheckMultiSig = byte(0xAE) 46 | OpPushData1 = byte(0x4C) 47 | OpPushData2 = byte(0x4D) 48 | OpPushData3 = byte(0x4E) 49 | OpDrop = byte(0x75) 50 | ) 51 | 52 | var ( 53 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 54 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 55 | ) 56 | -------------------------------------------------------------------------------- /cxcTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package cxcTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | func byteArrayCompare(a, b []byte) bool { 10 | if len(a) != len(b) { 11 | return false 12 | } 13 | for index := 0; index < len(a); index++ { 14 | if a[index] != b[index] { 15 | return false 16 | } 17 | } 18 | return true 19 | } 20 | 21 | //reverseBytes endian reverse 22 | func reverseBytes(s []byte) []byte { 23 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 24 | s[i], s[j] = s[j], s[i] 25 | } 26 | return s 27 | } 28 | 29 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 30 | func reverseHexToBytes(hexVar string) ([]byte, error) { 31 | if len(hexVar)%2 == 1 { 32 | return nil, errors.New("Invalid TxHash!") 33 | } 34 | ret, err := hex.DecodeString(hexVar) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return reverseBytes(ret), nil 39 | } 40 | 41 | //reverseBytesToHex change the endian of the input byte array then encode it to hex string 42 | func reverseBytesToHex(bytesVar []byte) string { 43 | return hex.EncodeToString(reverseBytes(bytesVar)) 44 | } 45 | 46 | //uint16ToLittleEndianBytes 47 | func uint16ToLittleEndianBytes(data uint16) []byte { 48 | tmp := [2]byte{} 49 | binary.LittleEndian.PutUint16(tmp[:], data) 50 | return tmp[:] 51 | } 52 | 53 | //littleEndianBytesToUint16 54 | func littleEndianBytesToUint16(data []byte) uint16 { 55 | return binary.LittleEndian.Uint16(data) 56 | } 57 | 58 | //uint32ToLittleEndianBytes 59 | func uint32ToLittleEndianBytes(data uint32) []byte { 60 | tmp := [4]byte{} 61 | binary.LittleEndian.PutUint32(tmp[:], data) 62 | return tmp[:] 63 | } 64 | 65 | //littleEndianBytesToUint32 66 | func littleEndianBytesToUint32(data []byte) uint32 { 67 | return binary.LittleEndian.Uint32(data) 68 | } 69 | 70 | //uint64ToLittleEndianBytes 71 | func uint64ToLittleEndianBytes(data uint64) []byte { 72 | tmp := [8]byte{} 73 | binary.LittleEndian.PutUint64(tmp[:], data) 74 | return tmp[:] 75 | } 76 | 77 | //littleEndianBytesToUint64 78 | func littleEndianBytesToUint64(data []byte) uint64 { 79 | return binary.LittleEndian.Uint64(data) 80 | } 81 | -------------------------------------------------------------------------------- /elastosTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package elastosTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | 7 | owcrypt "github.com/blocktree/go-owcrypt" 8 | ) 9 | 10 | type Vin struct { 11 | TxID string 12 | Vout uint16 13 | Address string 14 | } 15 | 16 | type Vout struct { 17 | AssetID string 18 | Amount uint64 19 | Address string 20 | } 21 | 22 | type TxHash struct { 23 | Address string 24 | Hash string 25 | } 26 | 27 | func CreateEmptyRawTransactionAndHash(vins []Vin, vouts []Vout) (string, []TxHash, error) { 28 | if vins == nil || len(vins) == 0 || vouts == nil || len(vouts) == 0 { 29 | return "", nil, errors.New("Miss inputs or outputs!") 30 | } 31 | tx, err := NewTransaction(vins, vouts) 32 | if err != nil { 33 | return "", nil, err 34 | } 35 | emptyTrans, hash := tx.GetEmptyAndHash() 36 | 37 | txHashes := []TxHash{} 38 | 39 | loop: 40 | for _, in := range vins { 41 | for _, txHash := range txHashes { 42 | if txHash.Address == in.Address { 43 | continue loop 44 | } 45 | } 46 | txHashes = append(txHashes, TxHash{Address: in.Address, Hash: hex.EncodeToString(hash)}) 47 | 48 | } 49 | 50 | return hex.EncodeToString(emptyTrans), txHashes, nil 51 | } 52 | 53 | func SignRawTransaction(hash string, privateKey []byte) ([]byte, error) { 54 | hashByte, err := hex.DecodeString(hash) 55 | if err != nil { 56 | return nil, errors.New("Invalid transaction hash!") 57 | } 58 | 59 | signature,_, retCode := owcrypt.Signature(privateKey, nil, hashByte, owcrypt.ECC_CURVE_SECP256R1) 60 | if retCode != owcrypt.SUCCESS { 61 | return nil, errors.New("Failed to sign transaction!") 62 | } 63 | 64 | return signature, nil 65 | } 66 | 67 | func VerifyAndCombineRawTransaction(emptyTrans string, sigPubs []SigPub) (bool, string) { 68 | 69 | txBytes, err := hex.DecodeString(emptyTrans) 70 | if err != nil { 71 | return false, "" 72 | } 73 | 74 | hash := owcrypt.Hash(txBytes, 0, owcrypt.HASH_ALG_SHA256) 75 | 76 | for _, sp := range sigPubs { 77 | publicKey := owcrypt.PointDecompress(sp.PublicKey, owcrypt.ECC_CURVE_SECP256R1)[1:] 78 | if owcrypt.SUCCESS != owcrypt.Verify(publicKey, nil, hash, sp.Signature, owcrypt.ECC_CURVE_SECP256R1) { 79 | return false, "" 80 | } 81 | } 82 | 83 | sigRaw, err := Sigpubs(sigPubs).ToBytes() 84 | if err != nil { 85 | return false, "" 86 | } 87 | 88 | return true, hex.EncodeToString(append(txBytes, sigRaw...)) 89 | } 90 | -------------------------------------------------------------------------------- /elastosTransaction/txIn.go: -------------------------------------------------------------------------------- 1 | package elastosTransaction 2 | 3 | import "errors" 4 | 5 | type Input struct { 6 | TxID []byte 7 | Vout []byte 8 | Sequence []byte 9 | } 10 | 11 | func (in Vin) NewInput() (*Input, error) { 12 | txidBytes, err := reverseHexToBytes(in.TxID) 13 | if err != nil { 14 | return nil, errors.New("Invalid previous transaction ID!") 15 | } 16 | vout := uint16ToLittleEndianBytes(in.Vout) 17 | 18 | return &Input{ 19 | TxID: txidBytes, 20 | Vout: vout, 21 | Sequence: []byte{0xff, 0xff, 0xff, 0xff}, 22 | }, nil 23 | } 24 | 25 | func (input Input) ToBytes() []byte { 26 | 27 | ret := []byte{} 28 | 29 | ret = append(ret, input.TxID...) 30 | ret = append(ret, input.Vout...) 31 | ret = append(ret, input.Sequence...) 32 | 33 | return ret 34 | } 35 | -------------------------------------------------------------------------------- /elastosTransaction/txOut.go: -------------------------------------------------------------------------------- 1 | package elastosTransaction 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | type Output struct { 8 | AssetID []byte 9 | Amount []byte 10 | Lock []byte 11 | ProgramHash []byte 12 | } 13 | 14 | func (out Vout) NewOutput() (*Output, error) { 15 | assetID, err := reverseHexToBytes(out.AssetID) 16 | if err != nil { 17 | return nil, errors.New("Invalid Asset ID!") 18 | } 19 | 20 | amount := uint64ToLittleEndianBytes(out.Amount) 21 | 22 | programHash, err := GetProgramHashFromAddress(out.Address) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return &Output{ 28 | AssetID: assetID, 29 | Amount: amount, 30 | Lock: []byte{0, 0, 0, 0}, 31 | ProgramHash: programHash, 32 | }, nil 33 | } 34 | 35 | func (output Output) ToBytes() []byte { 36 | ret := []byte{} 37 | 38 | ret = append(ret, output.AssetID...) 39 | ret = append(ret, output.Amount...) 40 | ret = append(ret, output.Lock...) 41 | ret = append(ret, output.ProgramHash...) 42 | 43 | return ret 44 | } 45 | -------------------------------------------------------------------------------- /elastosTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package elastosTransaction 2 | 3 | const ( 4 | TxTypeTransferAsset = byte(0x02) 5 | DefaultPayloadVersion = byte(0x00) 6 | AddressPrefix = byte(0x21) 7 | OP_CHECKSIG = byte(0xAC) 8 | AssetID_ELA = "a3d0eaa466df74983b5d7c543de6904f4c9418ead5ffd6d25814234a96db37b0" 9 | ) 10 | -------------------------------------------------------------------------------- /elastosTransaction/txSignature.go: -------------------------------------------------------------------------------- 1 | package elastosTransaction 2 | 3 | import "errors" 4 | 5 | type SigPub struct { 6 | PublicKey []byte 7 | Signature []byte 8 | } 9 | 10 | type Sigpubs []SigPub 11 | 12 | func (sp SigPub) ToBytes() ([]byte, error) { 13 | 14 | if sp.PublicKey == nil || len(sp.PublicKey) != 0x21 { 15 | return nil, errors.New("Miss public key!") 16 | } 17 | 18 | if sp.Signature == nil || len(sp.Signature) != 0x40 { 19 | return nil, errors.New("Miss signature data!") 20 | } 21 | 22 | ret := []byte{} 23 | ret = append(ret, 0x41, 0x40) 24 | ret = append(ret, sp.Signature...) 25 | ret = append(ret, 0x23, 0x21) 26 | ret = append(ret, sp.PublicKey...) 27 | ret = append(ret, OP_CHECKSIG) 28 | 29 | return ret, nil 30 | } 31 | 32 | func (sps Sigpubs) ToBytes() ([]byte, error) { 33 | ret := []byte{} 34 | 35 | ret = append(ret, uint64ToUvarint(uint64(len(sps)))...) 36 | for _, sp := range sps { 37 | data, err := sp.ToBytes() 38 | if err != nil { 39 | return nil, err 40 | } 41 | ret = append(ret, data...) 42 | } 43 | return ret, nil 44 | } 45 | -------------------------------------------------------------------------------- /elastosTransaction/txStruct.go: -------------------------------------------------------------------------------- 1 | package elastosTransaction 2 | 3 | import owcrypt "github.com/blocktree/go-owcrypt" 4 | 5 | type Transaction struct { 6 | TxType byte 7 | PayloadVersion byte 8 | Attributes []*[]byte 9 | Inputs []*Input 10 | Outputs []*Output 11 | LockTime []byte 12 | Signatures []*SigPub 13 | } 14 | 15 | func NewTransaction(vins []Vin, vouts []Vout) (*Transaction, error) { 16 | tx := Transaction{} 17 | 18 | tx.TxType = TxTypeTransferAsset 19 | tx.PayloadVersion = DefaultPayloadVersion 20 | tx.Attributes = nil 21 | 22 | for _, in := range vins { 23 | input, err := in.NewInput() 24 | if err != nil { 25 | return nil, err 26 | } 27 | tx.Inputs = append(tx.Inputs, input) 28 | } 29 | 30 | for _, out := range vouts { 31 | output, err := out.NewOutput() 32 | if err != nil { 33 | return nil, err 34 | } 35 | tx.Outputs = append(tx.Outputs, output) 36 | } 37 | 38 | tx.LockTime = []byte{0, 0, 0, 0} 39 | 40 | tx.Signatures = nil 41 | 42 | return &tx, nil 43 | } 44 | 45 | func (tx Transaction) GetEmptyAndHash() ([]byte, []byte) { 46 | txBytes := []byte{} 47 | 48 | txBytes = append(txBytes, tx.TxType, tx.PayloadVersion, 0x00) // 0x00 for attributes count 49 | 50 | txBytes = append(txBytes, uint64ToUvarint(uint64(len(tx.Inputs)))...) // input count 51 | 52 | for _, input := range tx.Inputs { 53 | txBytes = append(txBytes, input.ToBytes()...) 54 | } 55 | 56 | txBytes = append(txBytes, uint64ToUvarint(uint64(len(tx.Outputs)))...) // output count 57 | for _, output := range tx.Outputs { 58 | txBytes = append(txBytes, output.ToBytes()...) 59 | } 60 | 61 | txBytes = append(txBytes, tx.LockTime...) 62 | 63 | return txBytes, owcrypt.Hash(txBytes, 0, owcrypt.HASH_ALG_SHA256) 64 | } 65 | -------------------------------------------------------------------------------- /elastosTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package elastosTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | //reverseBytes endian reverse 10 | func reverseBytes(s []byte) []byte { 11 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 12 | s[i], s[j] = s[j], s[i] 13 | } 14 | return s 15 | } 16 | 17 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 18 | func reverseHexToBytes(hexVar string) ([]byte, error) { 19 | if len(hexVar)%2 == 1 { 20 | return nil, errors.New("Invalid TxHash!") 21 | } 22 | ret, err := hex.DecodeString(hexVar) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return reverseBytes(ret), nil 27 | } 28 | 29 | //uint64ToLittleEndianBytes 30 | func uint64ToLittleEndianBytes(data uint64) []byte { 31 | tmp := [8]byte{} 32 | binary.LittleEndian.PutUint64(tmp[:], data) 33 | return tmp[:] 34 | } 35 | 36 | //littleEndianBytesToUint64 37 | func littleEndianBytesToUint64(data []byte) uint64 { 38 | return binary.LittleEndian.Uint64(data) 39 | } 40 | 41 | //uint32ToLittleEndianBytes 42 | func uint32ToLittleEndianBytes(data uint32) []byte { 43 | tmp := [4]byte{} 44 | binary.LittleEndian.PutUint32(tmp[:], data) 45 | return tmp[:] 46 | } 47 | 48 | //littleEndianBytesToUint32 49 | func littleEndianBytesToUint32(data []byte) uint32 { 50 | return binary.LittleEndian.Uint32(data) 51 | } 52 | 53 | //uint16ToLittleEndianBytes 54 | func uint16ToLittleEndianBytes(data uint16) []byte { 55 | tmp := [2]byte{} 56 | binary.LittleEndian.PutUint16(tmp[:], data) 57 | return tmp[:] 58 | } 59 | 60 | //littleEndianBytesToUint16 61 | func littleEndianBytesToUint16(data []byte) uint16 { 62 | return binary.LittleEndian.Uint16(data) 63 | } 64 | 65 | func uint64ToUvarint(x uint64) []byte { 66 | ret := [8]byte{} 67 | 68 | len := binary.PutUvarint(ret[:], x) 69 | 70 | if len == 0 { 71 | return nil 72 | } 73 | return ret[:len] 74 | } 75 | 76 | func uvarintToUint64(buf []byte) uint64 { 77 | if buf == nil || len(buf) == 0x00 { 78 | return 0 79 | } 80 | 81 | ret, length := binary.Uvarint(buf) 82 | 83 | if length != len(buf) { 84 | return 0 85 | } 86 | 87 | return ret 88 | } 89 | -------------------------------------------------------------------------------- /eosSignature/signature.go: -------------------------------------------------------------------------------- 1 | package eosSignature 2 | 3 | import ( 4 | "errors" 5 | 6 | owcrypt "github.com/blocktree/go-owcrypt" 7 | ) 8 | 9 | func signRFC6979(privateKey, hash []byte, nonce int) ([]byte, error) { 10 | k := generateRandomFromNonce(privateKey, hash, nonce) 11 | 12 | if owcrypt.PreprocessRandomNum(k) != owcrypt.SUCCESS { 13 | return nil, errors.New("Failed to set random!") 14 | } 15 | sig, ret := owcrypt.Signature(privateKey, nil, 0, hash, 32, owcrypt.ECC_CURVE_SECP256K1|owcrypt.NOUNCE_OUTSIDE_FLAG) 16 | if ret != owcrypt.SUCCESS { 17 | return nil, errors.New("Failed to signature!") 18 | } 19 | return sig, nil 20 | } 21 | 22 | func equals(a, b []byte) bool { 23 | if len(a) != len(b) { 24 | return false 25 | } 26 | for index := 0; index < len(a); index++ { 27 | if a[index] != b[index] { 28 | return false 29 | } 30 | } 31 | return true 32 | } 33 | 34 | func makeCompact(sig, publicKey, hash []byte) ([]byte, error) { 35 | for i := 0; i < 2; i++ { 36 | tmp := append(sig, byte(i)) 37 | pk, ret := owcrypt.RecoverPubkey(tmp, hash, owcrypt.ECC_CURVE_SECP256K1) 38 | if ret == owcrypt.SUCCESS && equals(pk, publicKey) { 39 | result := make([]byte, 1, 2*32+1) 40 | result[0] = 27 + byte(i) 41 | result[0] += 4 42 | // Not sure this needs rounding but safer to do so. 43 | curvelen := (256 + 7) / 8 44 | 45 | // Pad R and S to curvelen if needed. 46 | bytelen := (256 + 7) / 8 47 | if bytelen < curvelen { 48 | result = append(result, 49 | make([]byte, curvelen-bytelen)...) 50 | } 51 | result = append(result, sig[:32]...) 52 | 53 | bytelen = (256 + 7) / 8 54 | if bytelen < curvelen { 55 | result = append(result, 56 | make([]byte, curvelen-bytelen)...) 57 | } 58 | result = append(result, sig[32:]...) 59 | 60 | return result, nil 61 | } 62 | } 63 | 64 | return nil, errors.New("no valid solution for pubkey found") 65 | } 66 | 67 | func isCanonical(compactSig []byte) bool { 68 | d := compactSig 69 | t1 := (d[1] & 0x80) == 0 70 | t2 := !(d[1] == 0 && ((d[2] & 0x80) == 0)) 71 | t3 := (d[33] & 0x80) == 0 72 | t4 := !(d[33] == 0 && ((d[34] & 0x80) == 0)) 73 | return t1 && t2 && t3 && t4 74 | } 75 | 76 | func SignCanonical(privateKey, hash []byte) ([]byte, error) { 77 | 78 | for i := 0; i < 25; i++ { 79 | sig, err := signRFC6979(privateKey, hash, i) 80 | if err != nil { 81 | return nil, err 82 | } 83 | 84 | publicKey, ret := owcrypt.GenPubkey(privateKey, owcrypt.ECC_CURVE_SECP256K1) 85 | if ret != owcrypt.SUCCESS { 86 | return nil, errors.New("Invalid private key!") 87 | } 88 | compactSig, err := makeCompact(sig, publicKey, hash) 89 | if err != nil { 90 | continue 91 | } 92 | 93 | if isCanonical(compactSig) { 94 | return compactSig, nil 95 | } 96 | } 97 | return nil, errors.New("couldn't find a canonical signature") 98 | } 99 | -------------------------------------------------------------------------------- /eosSignature/signature_test.go: -------------------------------------------------------------------------------- 1 | package eosSignature 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_signature(t *testing.T) { 10 | /**---test 1-----**/ 11 | //standard result:9e3813f1beb98607ed0cfa9199a41000ee12ac57f551d46ed944705a2cfad52e713d2ba16e48e58f4c427df185bd73b7142afed19c37752978acf7417aa517af01 12 | //hash:=[]byte{0xce,0x99,0x90,0x1c,0x86,0xcb,0x50,0x0b,0xb1,0xd1,0x35,0xb6,0xab,0x6b,0xf1,0xd9,0x18,0xde,0x2a,0x7b,0xc5,0x54,0xc9,0xad,0xc2,0x5b,0x5a,0x09,0xd2,0x5c,0xf3,0x03} 13 | //prikey:=[]byte{0xc1,0xa1,0x19,0xf1,0x63,0xde,0xf0,0x72,0xf2,0x1a,0x1b,0x3b,0x96,0x05,0x2c,0x65,0x9d,0x71,0x77,0x3f,0x20,0x75,0xec,0x00,0x5d,0x4a,0xd8,0x49,0x24,0x71,0x7e,0x6a} 14 | //pubkey:=[]byte{0xD7,0xE4,0xC0,0x1B,0x63,0xAA,0xBE,0x17,0x75,0x89,0xFA,0x90,0x05,0xD6,0xC7,0xB1,0x18,0x22,0x83,0xC8,0x04,0xD8,0x43,0xF5,0xF4,0xC4,0xD6,0x16,0x17,0xBC,0x9F,0x23,0xFD,0x27,0xEB,0xDD,0x1E,0x67,0xFF,0x6C,0x93,0xA2,0x56,0x11,0xB5,0xC4,0xC3,0xA3,0xDD,0x7C,0x87,0xBD,0x6E,0x3E,0x63,0x62,0x71,0x7F,0x5E,0x67,0x0D,0xE3,0x66,0x32} 15 | /**----test 2-----**/ 16 | //standard result:4d98f9b5ac76d314ba249a37d64de347a7f406132c8f7624b69c74b5badf9743668c89edfc1743cd8bd58fe935383dd8d4b72b25ac21112f66d45dbebcc7b1af00 17 | //hash:=[]byte{0xA4,0x4C,0x69,0x32,0x00,0xC3,0x7B,0x00,0x32,0x68,0x76,0x27,0x17,0x6E,0x41,0xDF,0xAC,0xC9,0x53,0xCC,0x77,0xEB,0x97,0x63,0x81,0xCD,0xB7,0xA6,0x6B,0x17,0x21,0x58} 18 | //prikey:=[]byte{0xBC,0xB9,0x71,0xDD,0x9A,0x73,0x1B,0x66,0xA4,0x25,0x51,0x7F,0x1F,0x02,0xC8,0xC3,0xAF,0x46,0xAF,0x74,0xFF,0x2F,0x62,0xF4,0xEF,0x21,0x14,0x70,0x41,0xC6,0xBB,0xA5} 19 | //pubkey:=[]byte{0x3D,0x91,0xE1,0xF9,0xC2,0x3E,0xAA,0x38,0x09,0x7C,0x87,0xAC,0xC0,0x6F,0x02,0xC9,0x57,0xDD,0x98,0x8F,0x0A,0x24,0x76,0x36,0xCD,0xDD,0x0F,0x91,0x43,0xA4,0xA9,0x5D,0x6A,0x08,0x19,0x58,0x6E,0xE3,0xF3,0xC7,0x31,0xA2,0x76,0xEF,0x74,0x2B,0xEF,0xB1,0xAE,0x61,0x5B,0xBF,0x48,0xCE,0x7D,0xD2,0xA6,0xE8,0x91,0x67,0x63,0x2F,0xE9,0x73} 20 | /**----test 3-----**/ 21 | //standard result: a8064a1b1eab7f28bd0f26cdbdf2315e280b17eacab834bc27ab86e40307a9822e2b6bc2901fa439ce408dd13ff7ee930af51e47fc362bb8e44977e7009d1b5f00 22 | hash := []byte{0xA4, 0x4C, 0x69, 0x32, 0x00, 0xC3, 0x7B, 0x00, 0x32, 0x68, 0x76, 0x27, 0x17, 0x6E, 0x41, 0xDF, 0xAC, 0xC9, 0x53, 0xCC, 0x77, 0xEB, 0x97, 0x63, 0x81, 0xCD, 0xB7, 0xA6, 0x6B, 0x17, 0x21, 0x58} 23 | prikey := []byte{0xA8, 0xDE, 0xCB, 0xDF, 0x2A, 0x5C, 0x92, 0xF8, 0xD8, 0xFC, 0x4D, 0x53, 0x36, 0x7F, 0x3A, 0x21, 0x55, 0x84, 0xB0, 0xDD, 0xA9, 0x2E, 0xFC, 0x30, 0xBE, 0x89, 0x51, 0x44, 0xD3, 0xD5, 0x6F, 0x97} 24 | //pubkey :=[]byte{0x0B,0xF0,0xAE,0xD1,0x07,0x11,0xCC,0xE9,0xC0,0x7D,0x6F,0xFB,0xB4,0xCD,0x9D,0x93,0xA0,0x0B,0xF5,0x3A,0x97,0x22,0x08,0x1E,0x5A,0x1A,0x6C,0xB5,0x94,0xB0,0xF0,0x4E,0xAF,0x97,0x8B,0x8F,0x7B,0x7F,0xCA,0xFE,0xEF,0x85,0xA3,0x6F,0xBA,0xF6,0x6C,0x6F,0xA0,0xEA,0xC0,0x5D,0x46,0x8E,0x83,0x41,0x80,0xDE,0x34,0xCB,0x74,0xDD,0x45,0xCA} 25 | sig, err := SignCanonical(prikey, hash) 26 | if err != nil { 27 | t.Error("ETH sign fail") 28 | } else { 29 | fmt.Println("eth sign result:", hex.EncodeToString(sig)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /evaioTransaction/bech32_test.go: -------------------------------------------------------------------------------- 1 | package evaioTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_ATOMAddress(t *testing.T) { 10 | hash, _ := hex.DecodeString("3335a8768bf87fbd1e554e71a82da2809110e190") 11 | address := Bech32Encode("cosmos", ATOMBech32Alphabet, hash) 12 | 13 | if address != "cosmos1xv66sa5tlplm68j4fec6stdzszg3pcvswag06j" { 14 | t.Error("atom address encode failed!") 15 | } else { 16 | fmt.Println("atom address encode success!") 17 | fmt.Println(address) 18 | } 19 | 20 | check, err := Bech32Decode(address) 21 | 22 | if err != nil { 23 | t.Error("atom address decode failed!") 24 | } else { 25 | for index := 0; index < 20; index++ { 26 | if check[index] != hash[index] { 27 | t.Error("atom address decode failed!") 28 | } 29 | } 30 | fmt.Println("atom address decode success!") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /evaioTransaction/coin.go: -------------------------------------------------------------------------------- 1 | package evaioTransaction 2 | 3 | import ( 4 | "math/big" 5 | "strconv" 6 | ) 7 | 8 | type Int struct { 9 | i *big.Int 10 | } 11 | 12 | type Coin struct { 13 | Amount string `json:"amount"` 14 | Denom string `json:"denom"` 15 | } 16 | type Coins []Coin 17 | 18 | func NewInt(n int64) Int { 19 | return Int{big.NewInt(n)} 20 | } 21 | 22 | func NewIntFromBigInt(i *big.Int) Int { 23 | if i.BitLen() > 255 { 24 | panic("NewIntFromBigInt() out of bound") 25 | } 26 | return Int{i} 27 | } 28 | 29 | func lt(i *big.Int, i2 *big.Int) bool { return i.Cmp(i2) == -1 } 30 | 31 | func (i Int) LT(i2 Int) bool { 32 | return lt(i.i, i2.i) 33 | } 34 | 35 | func ZeroInt() Int { return Int{big.NewInt(0)} } 36 | 37 | func NewCoin(denom string, amount int64) Coin { 38 | 39 | if amount == 0 { 40 | return Coin{} 41 | } 42 | 43 | return Coin{ 44 | Denom: denom, 45 | Amount: strconv.FormatInt(amount, 10), 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /evaioTransaction/fee.go: -------------------------------------------------------------------------------- 1 | package evaioTransaction 2 | 3 | import "strconv" 4 | 5 | type FeeStruct struct { 6 | Amount Coins `json:"amount"` 7 | Gas string `json:"gas"` 8 | } 9 | 10 | func NewStdFee(gas int64, amount Coins) FeeStruct { 11 | 12 | if amount == nil { 13 | return FeeStruct{ 14 | Amount: Coins{}, 15 | Gas: strconv.FormatInt(gas, 10), 16 | } 17 | } 18 | 19 | return FeeStruct{ 20 | Amount: amount, 21 | Gas: strconv.FormatInt(gas, 10), 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /evaioTransaction/json_test.go: -------------------------------------------------------------------------------- 1 | package evaioTransaction 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_json(t *testing.T) { 10 | tx := TxStruct{ 11 | AccountNumber: "1858", 12 | ChainID: "gaia-13003", 13 | Fee: FeeStruct{ 14 | Amount: Coins{}, 15 | Gas: "200000", 16 | }, 17 | Memo: "", 18 | Message: []Message{NewMessage("cosmos-sdk/MsgSend", NewMsgSend("cosmos1x9rdj3pgk9l3fvuj0fzxwa38vz276ljcysewnn", "cosmos1xv66sa5tlplm68j4fec6stdzszg3pcvswag06j", Coins{NewCoin("muon", 1000000)}))}, 19 | Sequence: "2", 20 | } 21 | 22 | b, err := json.Marshal(tx) 23 | if err != nil { 24 | fmt.Println(err) 25 | } 26 | fmt.Println(string(b)) 27 | } 28 | -------------------------------------------------------------------------------- /evaioTransaction/message.go: -------------------------------------------------------------------------------- 1 | package evaioTransaction 2 | 3 | type MsgSend struct { 4 | Amount Coins `json:"amount"` 5 | FromAddress string `json:"from_address"` 6 | ToAddress string `json:"to_address"` 7 | } 8 | 9 | type Message struct { 10 | Type string `json:"type"` 11 | Value MsgSend `json:"value"` 12 | } 13 | 14 | func NewMsgSend(fromAddr, toAddr string, amount Coins) MsgSend { 15 | return MsgSend{FromAddress: fromAddr, ToAddress: toAddr, Amount: amount} 16 | } 17 | 18 | func NewMessage(msgType string, value MsgSend) Message { 19 | return Message{Type: msgType, Value: value} 20 | } 21 | -------------------------------------------------------------------------------- /evaioTransaction/txSend.go: -------------------------------------------------------------------------------- 1 | package evaioTransaction 2 | 3 | import "encoding/base64" 4 | 5 | type Pub struct { 6 | Type string `json:"type"` 7 | Value string `json:"value"` 8 | } 9 | 10 | func NewPub(pubkey []byte, curveType string) Pub { 11 | return Pub{ 12 | Type: curveType, 13 | Value: base64.StdEncoding.EncodeToString(pubkey), 14 | } 15 | } 16 | 17 | type Sig struct { 18 | Signature string `json:"signature"` 19 | Pubkey Pub `json:"pub_key"` 20 | //AccountNumber string `json:"account_number"` 21 | //Sequence string `json:"sequence"` 22 | } 23 | 24 | func NewSig(signature []byte, pubkey Pub) Sig { 25 | return Sig{ 26 | Signature: base64.StdEncoding.EncodeToString(signature), 27 | Pubkey: pubkey, 28 | //AccountNumber: accountNumber, 29 | //Sequence: sequence, 30 | } 31 | } 32 | 33 | type Tx struct { 34 | Message []Message `json:"msg"` 35 | Fee FeeStruct `json:"fee"` 36 | Memo string `json:"memo"` 37 | Signature []Sig `json:"signatures"` 38 | } 39 | 40 | func NewTx(message []Message, memo string, fee FeeStruct, signature []Sig) Tx { 41 | return Tx{ 42 | Message: message, 43 | Fee: fee, 44 | Memo: memo, 45 | Signature: signature, 46 | } 47 | } 48 | 49 | type TxSend struct { 50 | Mode string `json:"mode"` 51 | Tx Tx `json:"tx"` 52 | 53 | } 54 | 55 | func NewTxSend(tx Tx, mode string) TxSend { 56 | return TxSend{ 57 | Tx: tx, 58 | Mode: mode, 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /evaioTransaction/txStruct.go: -------------------------------------------------------------------------------- 1 | package evaioTransaction 2 | 3 | import "strconv" 4 | 5 | type TxStruct struct { 6 | AccountNumber string `json:"account_number"` 7 | ChainID string `json:"chain_id"` 8 | Fee FeeStruct `json:"fee"` 9 | Memo string `json:"memo"` 10 | Message []Message `json:"msgs"` 11 | Sequence string `json:"sequence"` 12 | } 13 | 14 | func NewTxStruct(chainID, memo string, accountNumber, sequence int, fee *FeeStruct, message []Message) TxStruct { 15 | if fee.Amount[0].Amount == "" { 16 | fee.Amount = Coins{} 17 | } 18 | return TxStruct{ 19 | AccountNumber: strconv.FormatInt(int64(accountNumber), 10), 20 | ChainID: chainID, 21 | Fee: *fee, 22 | Memo: memo, 23 | Message: message, 24 | Sequence: strconv.FormatInt(int64(sequence), 10), 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /evaioTransaction/utils.go: -------------------------------------------------------------------------------- 1 | package evaioTransaction 2 | -------------------------------------------------------------------------------- /example_owkeychain.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | 7 | "github.com/blocktree/go-owcdrivers/owkeychain" 8 | ) 9 | 10 | func main() { 11 | seed := [32]byte{0x95, 0x59, 0xdb, 0xab, 0xf4, 0xd0, 0xb9, 0xf8, 0xae, 0x9a, 0x09, 0x5c, 0x93, 0x0e, 0xed, 0xe9, 0x32, 0xa5, 0x14, 0x76, 0x51, 0x86, 0xf8, 0xeb, 0x6d, 0xc3, 0x61, 0x6d, 0xcd, 0xf6, 0x68, 0xde} 12 | 13 | //获取比特币的扩展的根公钥 14 | //绝对路径为 --- m/44'/88'/0' 15 | btcRootPub, err := owkeychain.GetCoinRootPublicKey(seed[:], owkeychain.Bitcoin) 16 | 17 | if err != nil { 18 | fmt.Println("fail") 19 | } else { 20 | fmt.Println("-----------------------------------------------------------------") 21 | } 22 | 23 | //将根公钥编码成owpubXXXX格式 24 | owkey := btcRootPub.OWEncode() 25 | fmt.Println("owpub格式开头的根公钥") 26 | fmt.Println(owkey) 27 | fmt.Println("-----------------------------------------------------------------") 28 | 29 | //通过根公钥扩展子公钥 30 | //扩展10个,并转为P2PK地址 31 | index := uint32(0) 32 | 33 | for ; index < 10; index++ { 34 | tmpKey, err := btcRootPub.DerivedPublicKeyFromSerializes(index) 35 | if err != nil { 36 | fmt.Println("fail") 37 | } else { 38 | fmt.Println("第", index+1, "个地址:", owkeychain.Base58checkEncode(tmpKey.GetPublicKeyBytes(), owkeychain.BitcoinPubkeyPrefix)) 39 | 40 | } 41 | } 42 | fmt.Println("-----------------------------------------------------------------") 43 | 44 | //现在要获取第4个子公钥的私钥来进行签名,index = 3 45 | prikey, err := owkeychain.DerivedPrivateKeyBytes(seed[:], owkeychain.Bitcoin, uint32(3)) 46 | if err != nil { 47 | fmt.Println("fail") 48 | } else { 49 | fmt.Println("第4个地址对应的私钥", hex.EncodeToString(prikey[:])) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /fiiiTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package fiiiTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | // 交易费计算 10 | // ((262*inputCount + 101*outputCount + 68)/1024.0) * FeePerKB 11 | 12 | func Test_send(t *testing.T) { 13 | //本案例旨在复现链上交易 CE427FC1CF51EF1C72BAF0C97D7547A47D8E564EA662F36AA57D09F958164CED 14 | //如果想要结果完全一致,需要将 transaction.go 文件中的第36行替换为如下: 15 | //txMsg.Timestamp = int64(1546443912218) 16 | // 使用prefix区分主网和测试网 17 | prefix := []byte{0x40, 0xe7, 0xe9, 0x15} 18 | 19 | txid := "5E36D1C2879780E8ABBE4B451DE9202F66D1F037BF3D4372BA463522459B4BD4" 20 | vout := 1 21 | 22 | to1 := "fiiimH1KXvvVxfuNAH97u3hzfFEmojvTdkJZXg" 23 | amount1 := int64(624000000000) 24 | to2 := "fiiimRS3TUtbAyTX3SYLxZ7rq5wB2bcT5Y2KFb" 25 | amount2 := int64(510934733923614) 26 | 27 | //构建输入 28 | inputs := []Vin{Vin{txid, vout}} 29 | 30 | //构建输出 31 | outputs := []Vout{Vout{prefix, to1, amount1}, Vout{prefix, to2, amount2}} 32 | 33 | //其他参数 34 | version := int(1) 35 | locktime := int64(0) 36 | expiredtime := int64(0) 37 | 38 | //创建交易单与待签消息 39 | emptyTrans, messages, err := CreateEmptyTransactionAndMessage(inputs, outputs, version, locktime, expiredtime) 40 | 41 | if err != nil { 42 | t.Error("Failed to create transaction! with error: \n", err) 43 | } else { 44 | fmt.Println("空交易单: \n", emptyTrans, "\n ") 45 | fmt.Println("待签消息: \n", messages[0], "\n ") 46 | } 47 | 48 | // 客户端签名 49 | prikey := []byte{0x84, 0x98, 0x23, 0xd2, 0x2d, 0x81, 0xe4, 0x9e, 0xb7, 0x19, 0x06, 0x6b, 0xcf, 0x7e, 0xd1, 0x73, 0xe6, 0x09, 0x48, 0x22, 0xb0, 0xea, 0x4e, 0x79, 0x3f, 0x1d, 0x85, 0x97, 0xa5, 0x06, 0x0d, 0x27} 50 | 51 | signature, err := SignTransactionMessage(messages[0], prikey) 52 | if err != nil { 53 | t.Error("Failed to sign! with error : \n", err) 54 | } else { 55 | // only for test,构建与链上一致数据 56 | signature = []byte{0xCF, 0x1B, 0x46, 0x15, 0x7E, 0xB1, 0xBA, 0x6E, 0x39, 0xCD, 0xA6, 0xF4, 0xE6, 0x42, 0x17, 0xCC, 0x34, 0xF6, 0xE0, 0xAE, 0xFE, 0x91, 0x07, 0xAA, 0xFF, 0x98, 0x47, 0x4B, 0xA6, 0x71, 0xBC, 0xB4, 0x8C, 0x7A, 0x7D, 0xE6, 0x74, 0x60, 0x9C, 0xA6, 0xC9, 0xEF, 0x1C, 0x14, 0x2A, 0x3F, 0x66, 0x60, 0x18, 0xFE, 0xFA, 0xB3, 0xEB, 0x78, 0xB2, 0x1C, 0x2E, 0xC0, 0x3B, 0xAB, 0xD9, 0x31, 0xB5, 0x0D} 57 | fmt.Println("签名结果: \n", hex.EncodeToString(signature)) 58 | } 59 | 60 | // 验证与交易单合并 61 | // 获取对应公钥 62 | pubkey := []byte{0x22, 0x35, 0x88, 0xFB, 0xFA, 0xF1, 0x04, 0x08, 0xF0, 0x42, 0x46, 0x90, 0x31, 0xDD, 0x6D, 0x06, 0x28, 0xCC, 0x9E, 0xDE, 0x9F, 0x31, 0x2A, 0xD1, 0x28, 0xA4, 0x31, 0xF4, 0x20, 0x49, 0x22, 0x42} 63 | 64 | // 填充结构体 65 | sigPubs := []SigPub{SigPub{signature, pubkey}} 66 | 67 | pass, signedTrans, err := VerifyAndCombineTransaction(emptyTrans, sigPubs) 68 | if err != nil { 69 | t.Error("Failed to combine!") 70 | } else { 71 | if pass != true { 72 | t.Error("Verify failed!") 73 | } else { 74 | fmt.Println("待发送交易单: \n", signedTrans) 75 | } 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /fiiiTransaction/txInput.go: -------------------------------------------------------------------------------- 1 | package fiiiTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | type InputMsg struct { 10 | OutputTransactionHash string `json:"OutputTransactionHash"` 11 | OutputIndex int `json:"OutputIndex"` 12 | Size int `json:"Size"` 13 | UnlockScript string `json:"UnlockScript"` 14 | message []byte 15 | } 16 | 17 | func (in Vin) NewInputMsg() (*InputMsg, error) { 18 | ret := InputMsg{} 19 | 20 | if len(in.TxID) != 64 { 21 | return nil, errors.New("Invalid UTXO!") 22 | } 23 | 24 | _, err := hex.DecodeString(in.TxID) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | ret.OutputTransactionHash = in.TxID 30 | ret.OutputIndex = in.Vout 31 | 32 | return &ret, nil 33 | } 34 | 35 | func (in Vin) genMessage() string { 36 | vout := make([]byte, 4) 37 | binary.BigEndian.PutUint32(vout[:], uint32(in.Vout)) 38 | return in.TxID + hex.EncodeToString(vout) 39 | } 40 | 41 | func (in InputMsg) genMessageBytes() ([]byte, error) { 42 | msg, err := hex.DecodeString(in.OutputTransactionHash) 43 | if err != nil { 44 | return nil, errors.New("Invalid UTXO ID!") 45 | } 46 | vout := make([]byte, 4) 47 | binary.BigEndian.PutUint32(vout[:], uint32(in.OutputIndex)) 48 | 49 | msg = append(msg, vout...) 50 | 51 | return msg, nil 52 | } 53 | 54 | func (in InputMsg) ToBytes() []byte { 55 | data, _ := hex.DecodeString(in.OutputTransactionHash) 56 | 57 | index := make([]byte, 4) 58 | size := make([]byte, 4) 59 | 60 | binary.BigEndian.PutUint32(index, uint32(in.OutputIndex)) 61 | binary.BigEndian.PutUint32(size, uint32(in.Size)) 62 | 63 | data = append(data, index...) 64 | data = append(data, size...) 65 | 66 | data = append(data, []byte(in.UnlockScript)...) 67 | 68 | return data 69 | } 70 | -------------------------------------------------------------------------------- /fiiiTransaction/txOutput.go: -------------------------------------------------------------------------------- 1 | package fiiiTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | "strings" 8 | ) 9 | 10 | type OutputMsg struct { 11 | Index int `json:"Index"` 12 | Amount int64 `json:"Amount"` 13 | Size int `json:"Size"` 14 | LockScript string `json:"LockScript"` 15 | } 16 | 17 | func (out Vout) NewOutputMsg(index int) (*OutputMsg, error) { 18 | ret := OutputMsg{} 19 | 20 | hash, err := DecodeCheck(out.Address, out.AddressPrefix) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | if out.Amount <= 0 { 26 | return nil, errors.New("Invalid amount to send!") 27 | } 28 | 29 | ret.Index = index 30 | ret.Amount = out.Amount 31 | ret.LockScript = genLockScript(hash) 32 | ret.Size = len(ret.LockScript) 33 | 34 | return &ret, nil 35 | } 36 | 37 | func genLockScript(hash []byte) string { 38 | return "OP_DUP OP_HASH160 " + strings.ToUpper(hex.EncodeToString(hash)) + " OP_EQUALVERIFY OP_CHECKSIG" 39 | } 40 | 41 | func (out OutputMsg) ToBytes() []byte { 42 | data := []byte{} 43 | 44 | index := make([]byte, 4) 45 | amount := make([]byte, 8) 46 | size := make([]byte, 4) 47 | 48 | binary.BigEndian.PutUint32(index, uint32(out.Index)) 49 | binary.BigEndian.PutUint64(amount, uint64(out.Amount)) 50 | binary.BigEndian.PutUint32(size, uint32(out.Size)) 51 | 52 | data = append(data, index...) 53 | data = append(data, amount...) 54 | data = append(data, size...) 55 | data = append(data, []byte(out.LockScript)...) 56 | 57 | return data 58 | } 59 | -------------------------------------------------------------------------------- /fiiiTransaction/txStruct.go: -------------------------------------------------------------------------------- 1 | package fiiiTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "strings" 7 | 8 | owcrypt "github.com/blocktree/go-owcrypt" 9 | ) 10 | 11 | type TransactionMsg struct { 12 | Version int `json:"Version"` 13 | Hash string `json:"Hash"` 14 | Timestamp int64 `json:"Timestamp"` 15 | LockTime int64 `json:"Locktime"` 16 | ExpiredTime int64 `json:"ExpiredTime"` 17 | InputCount int `json:"InputCount"` 18 | OutputCount int `json:"OutputCount"` 19 | Inputs []InputMsg `json:"Inputs"` 20 | Outputs []OutputMsg `json:"Outputs"` 21 | Size int `json:"Size"` 22 | } 23 | 24 | func (tm *TransactionMsg) Complete() { 25 | txBytes := []byte{} 26 | 27 | timestamp := make([]byte, 8) 28 | lockTime := make([]byte, 8) 29 | expiredTime := make([]byte, 8) 30 | totleInput := make([]byte, 4) 31 | totleOutput := make([]byte, 4) 32 | 33 | binary.BigEndian.PutUint64(timestamp, uint64(tm.Timestamp)) 34 | binary.BigEndian.PutUint64(lockTime, uint64(tm.LockTime)) 35 | binary.BigEndian.PutUint64(expiredTime, uint64(tm.ExpiredTime)) 36 | binary.BigEndian.PutUint32(totleInput, uint32(tm.InputCount)) 37 | binary.BigEndian.PutUint32(totleOutput, uint32(tm.OutputCount)) 38 | 39 | txBytes = append(txBytes, timestamp...) 40 | txBytes = append(txBytes, lockTime...) 41 | txBytes = append(txBytes, expiredTime...) 42 | txBytes = append(txBytes, totleInput...) 43 | 44 | for _, in := range tm.Inputs { 45 | txBytes = append(txBytes, in.ToBytes()...) 46 | } 47 | 48 | txBytes = append(txBytes, totleOutput...) 49 | 50 | for _, out := range tm.Outputs { 51 | txBytes = append(txBytes, out.ToBytes()...) 52 | } 53 | 54 | tm.Hash = strings.ToUpper(hex.EncodeToString(owcrypt.Hash(txBytes, 0, owcrypt.HASH_ALG_SHA256))) 55 | 56 | tm.Size = len(txBytes) + 4 /*version*/ + 32 /*hash*/ 57 | 58 | } 59 | -------------------------------------------------------------------------------- /fiiiTransaction/txUnlock.go: -------------------------------------------------------------------------------- 1 | package fiiiTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "strings" 7 | ) 8 | 9 | const libraryPrefix = "302A300506032B6570032100" 10 | 11 | type SigPub struct { 12 | Signature []byte 13 | Pubkey []byte 14 | } 15 | 16 | func (sp SigPub) GenUnlockScript() (string, error) { 17 | 18 | if sp.Signature == nil || len(sp.Signature) != 64 { 19 | return "", errors.New("Invalid signature data!") 20 | } 21 | if sp.Pubkey == nil || len(sp.Pubkey) != 32 { 22 | return "", errors.New("Invalid public key data!") 23 | } 24 | 25 | return strings.ToUpper(hex.EncodeToString(sp.Signature)) + "[ALL] " + libraryPrefix + strings.ToUpper(hex.EncodeToString(sp.Pubkey)), nil 26 | } 27 | -------------------------------------------------------------------------------- /filenetTransaction/address.go: -------------------------------------------------------------------------------- 1 | package filenetTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | ) 7 | 8 | func decodeAddress(address string) ([]byte, error) { 9 | 10 | hexBytes, err := Decode(address, BitcoinAlphabet) 11 | if err != nil { 12 | return nil, errors.New("Invalid address!") 13 | } 14 | 15 | addrBytes, err := hex.DecodeString(string(hexBytes)) 16 | if err != nil { 17 | return nil, errors.New("Invalid address!") 18 | } 19 | 20 | if len(addrBytes) != 20 { 21 | return nil, errors.New("Invalid address!") 22 | } 23 | 24 | return addrBytes, nil 25 | } 26 | 27 | func encodeAddress(hash []byte) (string, error) { 28 | 29 | if hash == nil || len(hash) != 20 { 30 | return "", errors.New("Miss hash data!") 31 | } 32 | 33 | return Encode([]byte(hex.EncodeToString(hash)), BitcoinAlphabet), nil 34 | } 35 | -------------------------------------------------------------------------------- /filenetTransaction/address_test.go: -------------------------------------------------------------------------------- 1 | package filenetTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "github.com/blocktree/go-owcrypt" 7 | "testing" 8 | ) 9 | 10 | func TestAddress(t *testing.T) { 11 | address := "3YZ92rqUpjYzXDrkL41Fz89LUMYRysktyWkayNcqBnrfBXwW1mCN9QL" 12 | 13 | addrBytes, err := decodeAddress(address) 14 | if err != nil { 15 | t.Error(err) 16 | } else { 17 | if hex.EncodeToString(addrBytes) != "33ddd597b306a6502cfaccdf54274bbf11d43f5a" { 18 | t.Error("address decode failed!") 19 | } 20 | } 21 | 22 | chk, _ := encodeAddress(addrBytes) 23 | 24 | if chk != address { 25 | t.Error("Failed!") 26 | } 27 | } 28 | 29 | func TestEncode(t *testing.T) { 30 | pubkey, _ := hex.DecodeString("03bc72331fb70aacc60783d43ce0ffde4ad7796c5a72c743d00f272f187f7fe0e2") 31 | 32 | pubkey = owcrypt.PointDecompress(pubkey, owcrypt.ECC_CURVE_SECP256K1)[1:] 33 | 34 | hash := owcrypt.Hash(pubkey, 0, owcrypt.HASH_ALG_KECCAK256)[12:] 35 | 36 | address, _ := encodeAddress(hash) 37 | 38 | fmt.Println(address) 39 | } -------------------------------------------------------------------------------- /filenetTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package filenetTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "encoding/json" 6 | "errors" 7 | "github.com/blocktree/go-owcrypt" 8 | ) 9 | 10 | type Vin struct { 11 | Address string 12 | } 13 | 14 | type Vout struct { 15 | Address string 16 | Amount uint64 17 | } 18 | 19 | type Vouts []Vout 20 | 21 | func CreateEmptyTransactionAndHash(in Vin, outs Vouts) (string, string, error) { 22 | 23 | tx, err := NewTxStruct(in, outs) 24 | if err != nil { 25 | return "", "", err 26 | } 27 | 28 | txBytes := tx.ToBytes() 29 | 30 | hash := owcrypt.Hash(txBytes, 0, owcrypt.HASH_ALG_SHA256) 31 | 32 | 33 | return hex.EncodeToString(txBytes), hex.EncodeToString(hash), nil 34 | } 35 | 36 | func SignTransaction(hash string, prikey []byte) (string, error) { 37 | if hash == "" || prikey == nil || len(prikey) != 32 { 38 | return "", errors.New("Invalid input data!") 39 | } 40 | 41 | hashBytes, err := hex.DecodeString(hash) 42 | if err != nil || len(hashBytes) != 32 { 43 | return "", errors.New("Invalid hash data!") 44 | } 45 | 46 | signature,_, retCode := owcrypt.Signature(prikey, nil, hashBytes, owcrypt.ECC_CURVE_SECP256K1) 47 | if retCode != owcrypt.SUCCESS { 48 | return "", errors.New("Failed to sign transaction!") 49 | } 50 | 51 | return hex.EncodeToString(signature), nil 52 | } 53 | 54 | func VerifyAndCombineTransaction(emptyTrans, signature, pubkey string) (string, bool) { 55 | 56 | tx, err := decodeRawTransaction(emptyTrans, signature) 57 | if err != nil { 58 | return "", false 59 | } 60 | 61 | hash, _ := hex.DecodeString(tx.TxId) 62 | sig, _ := hex.DecodeString(signature) 63 | 64 | pubBytes, err := hex.DecodeString(pubkey) 65 | if err != nil { 66 | return "", false 67 | } 68 | 69 | pubBytes = owcrypt.PointDecompress(pubBytes, owcrypt.ECC_CURVE_SECP256K1)[1:] 70 | 71 | if owcrypt.SUCCESS != owcrypt.Verify(pubBytes, nil, hash, sig, owcrypt.ECC_CURVE_SECP256K1) { 72 | return "", false 73 | } 74 | 75 | data, err := json.Marshal(tx) 76 | 77 | if err != nil { 78 | return "", false 79 | } 80 | return string(data), true 81 | } -------------------------------------------------------------------------------- /filenetTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package filenetTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestTrans(t *testing.T) { 10 | in := Vin{Address:"3YZ92rqUpjYzXDrkL41Fz89LUMYRysktyWkayNcqBnrfBXwW1mCN9QL"} 11 | 12 | out1 := Vout{ 13 | Address: "3YZ92rqUpjYzXDrkL41Fz89LUMYRysktyWkayNcqBnrfBXwW1mCN9QL", 14 | Amount: 1000000000, 15 | } 16 | 17 | out2 := Vout{ 18 | Address: "3YZ92rqUpjYzXDrkL41Fz89LUMYRysktyWkayNcqBnrfBXwW1mCN9QL", 19 | Amount: 2000000000, 20 | } 21 | 22 | outs := make(Vouts, 2) 23 | outs[0] = out1 24 | outs[1] = out2 25 | 26 | emptyTrans, hash, err := CreateEmptyTransactionAndHash(in, outs) 27 | if err != nil { 28 | t.Error(err) 29 | return 30 | } else { 31 | fmt.Println("empty : ", emptyTrans) 32 | fmt.Println("hash : ", hash) 33 | } 34 | 35 | prikey, _ := hex.DecodeString("80bc398d7c4a674daa977566c2e6cd5040520027e57fe806dfaa868df4cc43ab") 36 | 37 | signature, err := SignTransaction(hash, prikey) 38 | if err != nil { 39 | t.Error(err) 40 | return 41 | } else { 42 | //when time stamp => trans.TimeStamp = uint64ToLittleEndianBytes(1569831542) 43 | //signature = "fd443a533ac614edf03c1de61c28f8c438b22dc9b76ab3686ac6dcd7f11eff58114b1bed4a0859302552ad146d8ea6c1171c0a2784af4c1ff061beaba19aacc5" 44 | fmt.Println("sig : ", signature) 45 | } 46 | pubkey := "0226e56601a74eff3a96db55f940f8924ad9b54f86f8ea6205c22994bd599184ab" 47 | 48 | signedTrans, pass := VerifyAndCombineTransaction(emptyTrans, signature, pubkey) 49 | 50 | if pass { 51 | fmt.Println("signed : ", signedTrans) 52 | } else { 53 | t.Error("Failed!") 54 | } 55 | 56 | } -------------------------------------------------------------------------------- /filenetTransaction/txTo.go: -------------------------------------------------------------------------------- 1 | package filenetTransaction 2 | 3 | func (d Vouts) Len() int { 4 | return len(d) 5 | } 6 | 7 | func (d Vouts) Swap (i, j int) { 8 | d[i], d[j] = d[j], d[i] 9 | } 10 | 11 | func (d Vouts) Less(i, j int) bool { 12 | return d[i].Address < d[j].Address 13 | } -------------------------------------------------------------------------------- /filenetTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package filenetTransaction 2 | 3 | import "encoding/binary" 4 | 5 | //uint64ToLittleEndianBytes 6 | func uint64ToLittleEndianBytes(data uint64) []byte { 7 | tmp := [8]byte{} 8 | binary.LittleEndian.PutUint64(tmp[:], data) 9 | return tmp[:] 10 | } 11 | 12 | //littleEndianBytesToUint64 13 | func littleEndianBytesToUint64(data []byte) uint64 { 14 | return binary.LittleEndian.Uint64(data) 15 | } 16 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/blocktree/go-owcdrivers 2 | 3 | go 1.12 4 | 5 | require ( 6 | github.com/assetsadapterstore/tivalue-adapter v1.0.3 7 | github.com/astaxie/beego v1.11.1 8 | github.com/binance-chain/go-sdk v1.0.8 9 | github.com/blocktree/arkecosystem-adapter v1.0.4 10 | github.com/blocktree/bitshares-adapter v1.0.5 11 | github.com/blocktree/ddmchain-adapter v1.0.5 12 | github.com/blocktree/eosio-adapter v1.0.0 13 | github.com/blocktree/ethereum-adapter v1.1.10 14 | github.com/blocktree/futurepia-adapter v1.0.12 15 | github.com/blocktree/go-owcrypt v1.1.13 16 | github.com/blocktree/moacchain-adapter v1.0.3 17 | github.com/blocktree/nulsio-adapter v1.1.7 18 | github.com/blocktree/ontology-adapter v1.0.8 19 | github.com/blocktree/openwallet v1.5.4 20 | github.com/blocktree/rcproto-adapter v1.0.0 21 | github.com/blocktree/ripple-adapter v1.0.13 22 | github.com/blocktree/virtualeconomy-adapter v1.1.5 23 | github.com/blocktree/waykichain-adapter v1.0.3 24 | github.com/google/gofuzz v1.0.0 // indirect 25 | github.com/pkg/errors v0.8.1 26 | github.com/tendermint/tendermint v0.31.2-rc0 27 | github.com/tidwall/gjson v1.2.1 28 | ) 29 | 30 | // replace github.com/blocktree/go-owcrypt => /Users/heshuchao/workspace/go-workspace/go-owcrypt 31 | -------------------------------------------------------------------------------- /hcTransaction/base58_test.go: -------------------------------------------------------------------------------- 1 | package hcTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "github.com/blocktree/go-owcrypt" 7 | "testing" 8 | ) 9 | 10 | func TestDecode(t *testing.T) { 11 | hcaddr := "HsX5APCXSpR49Ei1xnivBbTW35VqzaTARrX" 12 | hchash,_:=Decode(hcaddr, BitcoinAlphabet) 13 | fmt.Println(hex.EncodeToString(hchash)) 14 | // 097fccee8d2713d60aaf8b67d8f8c54850ffacf18ff4 7e589864 15 | hash := owcrypt.Hash(hchash[:22], 0, owcrypt.HASH_ALG_BLAKE256) 16 | hash = owcrypt.Hash(hash, 0, owcrypt.HASH_ALG_BLAKE256) 17 | fmt.Println(hex.EncodeToString(hash)) 18 | 19 | 20 | data ,_ := hex.DecodeString("01000000015bc8f84fdb914c0dae04d57a78bd4d0468b8c1c0e5fb22e81ac9b3057acdc5cf0200000000ffffffff02e0e8fbb30400000000001976a9142397dfb31634cda1df23fe2f4c5bfaa4355d431f88ac2c2d06bd6500000000001976a914d51c3a8a8234538336c93001d14f4e28d4d8e80c88ac0000000000000000") 21 | 22 | hash = owcrypt.Hash(data, 0, owcrypt.HASH_ALG_BLAKE256) 23 | fmt.Println(hex.EncodeToString(hash)) 24 | 25 | pk,_:= hex.DecodeString("02289cfae6175cf7819007fd819a17da4514edbd428cee15267b14235bf970abf5") 26 | pk=owcrypt.PointDecompress(pk, owcrypt.ECC_CURVE_SECP256K1) 27 | fmt.Println(hex.EncodeToString(pk[1:])) 28 | 29 | pre, _ := hex.DecodeString("01000100015bc8f84fdb914c0dae04d57a78bd4d0468b8c1c0e5fb22e81ac9b3057acdc5cf0200000000ffffffff02e0e8fbb30400000000001976a9142397dfb31634cda1df23fe2f4c5bfaa4355d431f88ac2c2d06bd6500000000001976a914d51c3a8a8234538336c93001d14f4e28d4d8e80c88ac0000000000000000") 30 | 31 | witness,_ := hex.DecodeString("01000300011976a914d51c3a8a8234538336c93001d14f4e28d4d8e80c88ac") 32 | 33 | prehash := owcrypt.Hash(pre, 0, owcrypt.HASH_ALG_BLAKE256) 34 | whash := owcrypt.Hash(witness, 0, owcrypt.HASH_ALG_BLAKE256) 35 | 36 | data1,_ := hex.DecodeString("01000000") 37 | 38 | data1 = append(data1, prehash...) 39 | data1 = append(data1, whash...) 40 | fmt.Println(hex.EncodeToString(data1)) 41 | 42 | txhash := owcrypt.Hash(data1, 0, owcrypt.HASH_ALG_BLAKE256) 43 | fmt.Println(hex.EncodeToString(txhash)) 44 | 45 | 46 | } -------------------------------------------------------------------------------- /hcTransaction/txOut.go: -------------------------------------------------------------------------------- 1 | package hcTransaction 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | type TxOut struct { 9 | amount []byte 10 | lockScript []byte 11 | } 12 | 13 | func newTxOutForEmptyTrans(vout []Vout, addressPrefix AddressPrefix) ([]TxOut, error) { 14 | if vout == nil || len(vout) == 0 { 15 | return nil, errors.New("No address to send when create an empty transaction!") 16 | } 17 | var ret []TxOut 18 | var prefixStr string 19 | var p2pkhPrefixByte []byte 20 | var p2wpkhPrefixByte []byte 21 | prefixStr = addressPrefix.Bech32Prefix 22 | p2pkhPrefixByte = addressPrefix.P2PKHPrefix 23 | p2wpkhPrefixByte = addressPrefix.P2WPKHPrefix 24 | 25 | for _, v := range vout { 26 | amount := uint64ToLittleEndianBytes(v.Amount) 27 | 28 | if strings.Index(v.Address, prefixStr) == 0 { 29 | redeem, err := Bech32Decode(v.Address) 30 | if err != nil { 31 | return nil, errors.New("Invalid bech32 type address!") 32 | } 33 | 34 | redeem = append([]byte{byte(len(redeem))}, redeem...) 35 | redeem = append([]byte{0x00}, redeem...) 36 | 37 | ret = append(ret, TxOut{amount, redeem}) 38 | } 39 | 40 | prefix, hash, err := DecodeCheck(v.Address) 41 | if err != nil { 42 | return nil, errors.New("Invalid address to send!") 43 | } 44 | 45 | if len(hash) != 0x14 { 46 | return nil, errors.New("Invalid address to send!") 47 | } 48 | 49 | hash = append([]byte{byte(len(hash))}, hash...) 50 | hash = append([]byte{OpCodeHash160}, hash...) 51 | if byteArrayCompare(prefix, p2pkhPrefixByte) { 52 | hash = append(hash, OpCodeEqualVerify, OpCodeCheckSig) 53 | hash = append([]byte{OpCodeDup}, hash...) 54 | } else if byteArrayCompare(prefix, p2wpkhPrefixByte) { 55 | hash = append(hash, OpCodeEqual) 56 | } else { 57 | return nil, errors.New("Invalid address to send!") 58 | } 59 | 60 | ret = append(ret, TxOut{amount, hash}) 61 | } 62 | return ret, nil 63 | } 64 | 65 | func (out TxOut) toBytes() ([]byte, error) { 66 | if out.amount == nil || len(out.amount) != 8 { 67 | return nil, errors.New("Invalid amount for a transaction output!") 68 | } 69 | if out.lockScript == nil || len(out.lockScript) == 0 { 70 | return nil, errors.New("Invalid lock script for a transaction output!") 71 | } 72 | 73 | ret := []byte{} 74 | ret = append(ret, out.amount...) 75 | ret = append(ret, byte(len(out.lockScript))) 76 | ret = append(ret, out.lockScript...) 77 | 78 | return ret, nil 79 | } 80 | -------------------------------------------------------------------------------- /hcTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package hcTransaction 2 | 3 | type AddressPrefix struct { 4 | P2PKHPrefix []byte 5 | P2WPKHPrefix []byte 6 | Bech32Prefix string 7 | } 8 | var ( 9 | HCMainnetAddressPrefix = AddressPrefix{[]byte{0x09, 0x7f}, []byte{0x7f}, "Hs"} 10 | HCTestnetAddressPrefix = AddressPrefix{[]byte{0x0f, 0x21}, []byte{0x21}, "Ts"} 11 | 12 | ) 13 | 14 | const ( 15 | DefaultTxVersion = uint32(2) 16 | DefaultHashType = uint32(1) 17 | MaxScriptElementSize = 520 18 | ) 19 | 20 | const ( 21 | SequenceFinal = uint32(0xFFFFFFFF) 22 | SequenceMaxBip125RBF = uint32(0xFFFFFFFD) 23 | ) 24 | 25 | const ( 26 | SegWitSymbol = byte(0) 27 | SegWitVersion = byte(1) 28 | SigHashAll = byte(1) 29 | ) 30 | 31 | const ( 32 | OpCodeHash160 = byte(0xA9) 33 | OpCodeEqual = byte(0x87) 34 | OpCodeEqualVerify = byte(0x88) 35 | OpCodeCheckSig = byte(0xAC) 36 | OpCodeDup = byte(0x76) 37 | OpCode_1 = byte(0x51) 38 | OpCheckMultiSig = byte(0xAE) 39 | OpPushData1 = byte(0x4C) 40 | OpPushData2 = byte(0x4D) 41 | OpPushData3 = byte(0x4E) 42 | ) 43 | 44 | var ( 45 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 46 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 47 | ) 48 | -------------------------------------------------------------------------------- /hcTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package hcTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | func byteArrayCompare(a, b []byte) bool { 10 | if len(a) != len(b) { 11 | return false 12 | } 13 | for index := 0; index < len(a); index++ { 14 | if a[index] != b[index] { 15 | return false 16 | } 17 | } 18 | return true 19 | } 20 | 21 | //reverseBytes endian reverse 22 | func reverseBytes(s []byte) []byte { 23 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 24 | s[i], s[j] = s[j], s[i] 25 | } 26 | return s 27 | } 28 | 29 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 30 | func reverseHexToBytes(hexVar string) ([]byte, error) { 31 | if len(hexVar)%2 == 1 { 32 | return nil, errors.New("Invalid TxHash!") 33 | } 34 | ret, err := hex.DecodeString(hexVar) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return reverseBytes(ret), nil 39 | } 40 | 41 | //reverseBytesToHex change the endian of the input byte array then encode it to hex string 42 | func reverseBytesToHex(bytesVar []byte) string { 43 | return hex.EncodeToString(reverseBytes(bytesVar)) 44 | } 45 | 46 | //uint16ToLittleEndianBytes 47 | func uint16ToLittleEndianBytes(data uint16) []byte { 48 | tmp := [2]byte{} 49 | binary.LittleEndian.PutUint16(tmp[:], data) 50 | return tmp[:] 51 | } 52 | 53 | //littleEndianBytesToUint16 54 | func littleEndianBytesToUint16(data []byte) uint16 { 55 | return binary.LittleEndian.Uint16(data) 56 | } 57 | 58 | //uint32ToLittleEndianBytes 59 | func uint32ToLittleEndianBytes(data uint32) []byte { 60 | tmp := [4]byte{} 61 | binary.LittleEndian.PutUint32(tmp[:], data) 62 | return tmp[:] 63 | } 64 | 65 | //littleEndianBytesToUint32 66 | func littleEndianBytesToUint32(data []byte) uint32 { 67 | return binary.LittleEndian.Uint32(data) 68 | } 69 | 70 | //uint64ToLittleEndianBytes 71 | func uint64ToLittleEndianBytes(data uint64) []byte { 72 | tmp := [8]byte{} 73 | binary.LittleEndian.PutUint64(tmp[:], data) 74 | return tmp[:] 75 | } 76 | 77 | //littleEndianBytesToUint64 78 | func littleEndianBytesToUint64(data []byte) uint64 { 79 | return binary.LittleEndian.Uint64(data) 80 | } 81 | -------------------------------------------------------------------------------- /hypercashTransaction/omni_test.go: -------------------------------------------------------------------------------- 1 | package hypercashTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_1a234edc111a06d623210a25f3b6e8c41335bff4b2d5c048723a5cd9be530dc9(t *testing.T) { 10 | // 模拟链上交易 1a234edc111a06d623210a25f3b6e8c41335bff4b2d5c048723a5cd9be530dc9 11 | // 该交易发送1个ENTCASH 12 | // 包含一个输入 3个输出 13 | 14 | 15 | // 构建输入 16 | in := Vin{ 17 | TxID: "b8f7b641aef3c62e071a3d993d2bf886bc00afc25c60ce2a2c89937b44a6097c", 18 | Vout: 2, 19 | Tree: TxTreeRegular, 20 | Amount: 47797600, 21 | LockScript: "76a91441548ade23379f050d6e71ec115d870ee845aeec88ac", 22 | BlockHeight: 203754, 23 | BlockIndex: 1, 24 | } 25 | 26 | ins := []Vin{in} 27 | 28 | 29 | omniCost := uint64(1000000) // 最少向目标地址转入的金额 30 | // 构建输出 31 | // 转入地址 32 | to := Vout{ 33 | Amount: omniCost, 34 | PkScriptVersion:DefaultPkScriptVersion, 35 | Address: "HsNhaCPcJRPT5q2y6L7ViUg3JgjfayCEXfr", 36 | } 37 | 38 | //找零地址 39 | change := Vout{ 40 | Amount: 46769000, 41 | PkScriptVersion:DefaultPkScriptVersion, 42 | Address: "HsHbmqZLdhQmBGgPH9sCvXzphdA8JZQqkHY", 43 | } 44 | 45 | // omni币种 46 | propertyID := PropertyID_ENTCASH 47 | // omni金额 48 | amount := uint64(100000000) 49 | 50 | 51 | locktime := uint32(0) 52 | expiry := NoExpiryValue 53 | 54 | // 创建空交易单与待签哈希 55 | emptyTrans, hashes, err := CreateOmniEmptyTransactionAndHash(ins, &to, &change, amount, propertyID, locktime, expiry) 56 | 57 | if err != nil { 58 | t.Error("创建空交易单失败!") 59 | return 60 | } else { 61 | fmt.Println("空交易单为 : \n", emptyTrans) 62 | fmt.Println("待签哈希为 : \n", hashes[0]) 63 | } 64 | 65 | 66 | // 对交易签名 67 | prikey := []byte{0x80, 0xbc, 0x39, 0x8d, 0x7c, 0x4a, 0x67, 0x4d, 0xaa, 0x97, 0x75, 0x66, 0xc2, 0xe6, 0xcd, 0x50, 0x40, 0x52, 0x00, 0x27, 0xe5, 0x7f, 0xe8, 0x06, 0xdf, 0xaa, 0x86, 0x8d, 0xf4, 0xcc, 0x43, 0xab} 68 | 69 | signature, err := SignTransaction(hashes[0], prikey) 70 | if err != nil { 71 | t.Error("签名交易单失败!") 72 | return 73 | } else { 74 | // for test only 75 | signature = []byte{0xfe,0x81,0x83,0xfa,0x73,0xa0,0xc0,0x53,0xb4,0xcc,0x93,0xbb,0xea,0x69,0x4f,0x96,0xf3,0x19,0xcc,0x59,0xb3,0x88,0x73,0xc0,0x99,0xef,0x03,0x12,0xcc,0xfc,0x79,0x70,0x1f,0x98,0x5c,0x80,0xc7,0x2b,0x0f,0x78,0xe6,0x60,0x21,0xf9,0xf6,0x5e,0x65,0x40,0x2b,0x3f,0x32,0x85,0xb6,0x58,0x24,0x92,0xca,0x5b,0x3f,0xbd,0x19,0xf7,0xd8,0xb7} 76 | fmt.Println("签名结果为 : \n", hex.EncodeToString(signature)) 77 | } 78 | 79 | // 验证与合并交易单 80 | pubkey := []byte{0x02,0x9c,0x65,0x30,0x20,0xae,0xeb,0x00,0x7a,0x5e,0x0d,0x1c,0xfb,0x1f,0x28,0xd0,0x7d,0xd2,0x59,0xba,0xd4,0x5f,0xd0,0x8a,0xb6,0x89,0x59,0x27,0x99,0x59,0x1a,0xc3,0x49} 81 | 82 | // 构建签名体 83 | sigPubs := []*SigPub{ 84 | { 85 | Signature: signature, 86 | PublicKey: pubkey, 87 | }, 88 | } 89 | 90 | pass, signedTrans := VerifyAndCombineTransaction(emptyTrans, sigPubs) 91 | if pass { 92 | fmt.Println("合并之后的交易单为 : \n", signedTrans) 93 | } else { 94 | t.Error("验签失败!") 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /hypercashTransaction/txIn.go: -------------------------------------------------------------------------------- 1 | package hypercashTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "github.com/pkg/errors" 6 | ) 7 | 8 | type TxIn struct { 9 | TxID []byte 10 | Vout []byte 11 | TxTree byte 12 | Sequence []byte 13 | SignatureScript []byte 14 | LockScript []byte 15 | } 16 | 17 | func (in Vin) NewTxIn() (*TxIn, error) { 18 | var txIn TxIn 19 | 20 | txid, err := reverseHexToBytes(in.TxID) 21 | if err != nil || len(txid) != 32 { 22 | return nil, errors.New("Invalid previous transaction ID!") 23 | } 24 | txIn.TxID = txid 25 | 26 | txIn.Vout = uint32ToLittleEndianBytes(in.Vout) 27 | 28 | txIn.TxTree = TxTreeRegular 29 | 30 | txIn.Sequence = []byte{0xff, 0xff, 0xff, 0xff} 31 | 32 | txIn.SignatureScript = uint64ToLittleEndianBytes(in.Amount) 33 | txIn.SignatureScript = append(txIn.SignatureScript, uint32ToLittleEndianBytes(in.BlockHeight)...) 34 | txIn.SignatureScript = append(txIn.SignatureScript, uint32ToLittleEndianBytes(in.BlockIndex)...) 35 | 36 | lockscript,err := hex.DecodeString(in.LockScript) 37 | if err != nil { 38 | return nil, errors.New("Invalid lock script!") 39 | } 40 | 41 | txIn.LockScript = append([]byte{byte(len(lockscript))}, lockscript...) 42 | 43 | return &txIn, nil 44 | } 45 | 46 | func (in *TxIn) ToBytes() []byte { 47 | ret := make([]byte, 0) 48 | 49 | ret = append(ret, in.TxID...) 50 | ret = append(ret, in.Vout...) 51 | ret = append(ret, in.TxTree) 52 | ret = append(ret, in.Sequence...) 53 | 54 | return ret 55 | } -------------------------------------------------------------------------------- /hypercashTransaction/txOmni.go: -------------------------------------------------------------------------------- 1 | package hypercashTransaction 2 | 3 | 4 | func createPayloadSimpleSend(propertyID uint32, amount uint64) []byte { 5 | messageType := uint16(0) 6 | messageVer := uint16(0) 7 | 8 | ret := []byte{} 9 | 10 | ret = append(ret, uint16ToBigEndianBytes(messageVer)...) 11 | ret = append(ret, uint16ToBigEndianBytes(messageType)...) 12 | ret = append(ret, uint32ToBigEndianBytes(propertyID)...) 13 | ret = append(ret, uint64ToBigEndianBytes(amount)...) 14 | 15 | ret = append([]byte("omni"), ret...) 16 | ret = append([]byte{byte(len(ret))}, ret...) 17 | ret = append([]byte{OP_RETURN}, ret...) 18 | ret = append([]byte{byte(len(ret))}, ret...) 19 | return ret 20 | } -------------------------------------------------------------------------------- /hypercashTransaction/txOut.go: -------------------------------------------------------------------------------- 1 | package hypercashTransaction 2 | 3 | import "github.com/pkg/errors" 4 | 5 | type TxOut struct { 6 | Amount []byte 7 | PkScriptVersion []byte 8 | PKScript []byte 9 | } 10 | 11 | func (out Vout) NewTxOut() (*TxOut, error) { 12 | var txOut TxOut 13 | 14 | txOut.Amount = uint64ToLittleEndianBytes(out.Amount) 15 | txOut.PkScriptVersion = uint16ToLittleEndianBytes(DefaultPkScriptVersion) 16 | 17 | pkPrefix, pkhaash, err := DecodeCheck(out.Address) 18 | if err != nil { 19 | return nil, err 20 | } 21 | if len(pkPrefix) != len(PKHAddressPrefix) || len(pkhaash) != 20 { 22 | return nil, errors.New("Invalid address!") 23 | } 24 | for i, h := range pkPrefix { 25 | if h != PKHAddressPrefix[i] { 26 | return nil, errors.New("Invalid address!") 27 | } 28 | } 29 | 30 | pkhaash = append([]byte{0x19, 0x76, 0xA9, 0x14}, pkhaash...) 31 | pkhaash = append(pkhaash, []byte{0x88, 0xAC}...) 32 | 33 | txOut.PKScript = pkhaash 34 | 35 | return &txOut, nil 36 | } 37 | 38 | func (out *TxOut) ToBytes() []byte { 39 | ret := make([]byte, 0) 40 | 41 | ret = append(ret, out.Amount...) 42 | ret = append(ret, out.PkScriptVersion...) 43 | ret = append(ret, out.PKScript...) 44 | 45 | return ret 46 | } -------------------------------------------------------------------------------- /hypercashTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package hypercashTransaction 2 | 3 | const ( 4 | OP_RETURN byte = 0x6a 5 | TxTreeRegular byte = 0 6 | TxTreeStake byte = 1 7 | SigHashAll byte = 1 8 | 9 | DefaultTxVersion uint16 = 1 10 | DefaultPkScriptVersion uint16 = 0 11 | 12 | NoExpiryValue uint32 = 0 13 | 14 | ) 15 | 16 | 17 | const ( 18 | TxSerializeFull uint16 = iota 19 | TxSerializeNoWitness 20 | TxSerializeOnlyWitness 21 | TxSerializeWitnessSigning 22 | TxSerializeWitnessValueSigning 23 | ) 24 | 25 | 26 | const ( 27 | PropertyID_Omni uint32 = 1 28 | PropertyID_LEEK uint32 = 11 29 | PropertyID_YAX uint32 = 18 30 | PropertyID_ENTCASH uint32 = 19 31 | ) 32 | 33 | var ( 34 | PKHAddressPrefix = []byte{0x09, 0x7f} 35 | ) 36 | 37 | 38 | var ( 39 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 40 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 41 | ) 42 | -------------------------------------------------------------------------------- /hypercashTransaction/txSignature.go: -------------------------------------------------------------------------------- 1 | package hypercashTransaction 2 | 3 | import "math/big" 4 | 5 | 6 | type SigPub struct { 7 | Signature []byte 8 | PublicKey []byte 9 | Address string 10 | } 11 | 12 | func serilizeS(sig []byte) []byte { 13 | s := sig[32:] 14 | numS := new(big.Int).SetBytes(s) 15 | numHalfOrder := new(big.Int).SetBytes(HalfCurveOrder) 16 | if numS.Cmp(numHalfOrder) > 0 { 17 | numOrder := new(big.Int).SetBytes(CurveOrder) 18 | numS.Sub(numOrder, numS) 19 | 20 | s = numS.Bytes() 21 | if len(s) < 32 { 22 | for i := 0; i < 32-len(s); i++ { 23 | s = append([]byte{0x00}, s...) 24 | } 25 | } 26 | return append(sig[:32], s...) 27 | } 28 | return sig 29 | } 30 | 31 | 32 | func (sp SigPub) encodeSignatureToScript(sigType byte) []byte { 33 | r := sp.Signature[:32] 34 | s := sp.Signature[32:] 35 | if r[0]&0x80 == 0x80 { 36 | r = append([]byte{0x00}, r...) 37 | } else { 38 | for i := 0; i < 32; i++ { 39 | if r[0] == 0 && r[1]&0x80 != 0x80 { 40 | r = r[1:] 41 | } else { 42 | break 43 | } 44 | } 45 | } 46 | if s[0]&0x80 == 0x80 { 47 | s = append([]byte{0}, s...) 48 | } else { 49 | for i := 0; i < 32; i++ { 50 | if s[0] == 0 && s[1]&0x80 != 0x80 { 51 | s = s[1:] 52 | } else { 53 | break 54 | } 55 | } 56 | } 57 | 58 | r = append([]byte{byte(len(r))}, r...) 59 | r = append([]byte{0x02}, r...) 60 | s = append([]byte{byte(len(s))}, s...) 61 | s = append([]byte{0x02}, s...) 62 | 63 | rs := append(r, s...) 64 | rs = append([]byte{byte(len(rs))}, rs...) 65 | rs = append(rs, sigType) 66 | rs = append([]byte{0x30}, rs...) 67 | rs = append([]byte{byte(len(rs))}, rs...) 68 | 69 | return rs 70 | } 71 | 72 | func (sp SigPub) encodeToScript(sigType byte) []byte { 73 | pub := append([]byte{byte(len(sp.PublicKey))}, sp.PublicKey...) 74 | 75 | ret := append(sp.encodeSignatureToScript(sigType), pub...) 76 | return append([]byte{byte(len(ret))}, ret...) 77 | } -------------------------------------------------------------------------------- /hypercashTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package hypercashTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | "math" 8 | ) 9 | 10 | //reverseBytes endian reverse 11 | func reverseBytes(s []byte) []byte { 12 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 13 | s[i], s[j] = s[j], s[i] 14 | } 15 | return s 16 | } 17 | 18 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 19 | func reverseHexToBytes(hexVar string) ([]byte, error) { 20 | if len(hexVar)%2 == 1 { 21 | return nil, errors.New("Invalid TxHash!") 22 | } 23 | ret, err := hex.DecodeString(hexVar) 24 | if err != nil { 25 | return nil, err 26 | } 27 | return reverseBytes(ret), nil 28 | } 29 | 30 | //reverseBytesToHex change the endian of the input byte array then encode it to hex string 31 | func reverseBytesToHex(bytesVar []byte) string { 32 | return hex.EncodeToString(reverseBytes(bytesVar)) 33 | } 34 | 35 | func uint16ToBigEndianBytes(data uint16) []byte { 36 | tmp := [2]byte{} 37 | binary.BigEndian.PutUint16(tmp[:], data) 38 | return tmp[:] 39 | } 40 | 41 | //uint16ToLittleEndianBytes 42 | func uint16ToLittleEndianBytes(data uint16) []byte { 43 | tmp := [2]byte{} 44 | binary.LittleEndian.PutUint16(tmp[:], data) 45 | return tmp[:] 46 | } 47 | 48 | //littleEndianBytesToUint16 49 | func littleEndianBytesToUint16(data []byte) uint16 { 50 | return binary.LittleEndian.Uint16(data) 51 | } 52 | 53 | func uint32ToBigEndianBytes(data uint32) []byte { 54 | tmp := [4]byte{} 55 | binary.BigEndian.PutUint32(tmp[:], data) 56 | return tmp[:] 57 | } 58 | 59 | //uint32ToLittleEndianBytes 60 | func uint32ToLittleEndianBytes(data uint32) []byte { 61 | tmp := [4]byte{} 62 | binary.LittleEndian.PutUint32(tmp[:], data) 63 | return tmp[:] 64 | } 65 | 66 | //littleEndianBytesToUint32 67 | func littleEndianBytesToUint32(data []byte) uint32 { 68 | return binary.LittleEndian.Uint32(data) 69 | } 70 | 71 | func uint64ToBigEndianBytes(data uint64) []byte { 72 | tmp := [8]byte{} 73 | binary.BigEndian.PutUint64(tmp[:], data) 74 | return tmp[:] 75 | } 76 | 77 | //uint64ToLittleEndianBytes 78 | func uint64ToLittleEndianBytes(data uint64) []byte { 79 | tmp := [8]byte{} 80 | binary.LittleEndian.PutUint64(tmp[:], data) 81 | return tmp[:] 82 | } 83 | 84 | //littleEndianBytesToUint64 85 | func littleEndianBytesToUint64(data []byte) uint64 { 86 | return binary.LittleEndian.Uint64(data) 87 | } 88 | 89 | func varIntToBytes(val uint64) []byte { 90 | if val < 0xfd { 91 | return []byte{byte(val)} 92 | } 93 | 94 | if val <= math.MaxUint16 { 95 | return append([]byte{0xfd}, uint16ToLittleEndianBytes(uint16(val))...) 96 | } 97 | 98 | if val <= math.MaxUint32 { 99 | return append([]byte{0xfe}, uint32ToLittleEndianBytes(uint32(val))...) 100 | } 101 | 102 | return append([]byte{0xff}, uint64ToLittleEndianBytes(val)...) 103 | } -------------------------------------------------------------------------------- /mateverseTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package mateverseTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "github.com/blocktree/go-owcrypt" 7 | ) 8 | 9 | func GetInputsFromEmptyRawTransaction(emptyTrans string) ([]*TxInput, error){ 10 | trans, err := hex.DecodeString(emptyTrans) 11 | if err != nil { 12 | return nil, errors.New("Invalid empty raw transaction!") 13 | } 14 | inputs, _, err := decodeEmptyTx(trans) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | return inputs, nil 20 | } 21 | 22 | func GetSigHash(emptyTrans string, inputs *[]*TxInput) error { 23 | 24 | trans, err := hex.DecodeString(emptyTrans) 25 | if err != nil { 26 | return errors.New("Invalid empty raw transaction!") 27 | } 28 | _, inputsEnd, err := decodeEmptyTx(trans) 29 | if err != nil { 30 | return err 31 | } 32 | 33 | for i, input := range *inputs { 34 | if input.lockScript == "" { 35 | return errors.New("Miss lock script!") 36 | } 37 | lockScript := getHashFromLockScript(input.lockScript) 38 | if lockScript == "" || len(lockScript) < 50 { 39 | return errors.New("Invalid lock script!") 40 | } 41 | input.SetLockScript(lockScript) 42 | _, err = hex.DecodeString(input.txID) 43 | if err != nil || len(input.txID) != 64 { 44 | return errors.New("Invalid txid!") 45 | } 46 | hashBytes := getHashCalcBytes(*inputs, i) 47 | 48 | hashBytes = append(hashBytes, trans[inputsEnd:]...) 49 | hashBytes = append(hashBytes, uint32ToLittleEndianBytes(SigHashAll)...) 50 | 51 | input.hash = hex.EncodeToString(owcrypt.Hash(hashBytes, 0, owcrypt.HASH_ALG_DOUBLE_SHA256)) 52 | } 53 | 54 | return nil 55 | } 56 | 57 | func SignTransaction(hash string, prikey []byte) (string, error) { 58 | hashBytes, err := hex.DecodeString(hash) 59 | if err != nil || len(hashBytes) != 32 { 60 | return "", errors.New("Invalid transaction hash!") 61 | } 62 | if prikey == nil || len(prikey) != 32 { 63 | return "", errors.New("Invalid prikey!") 64 | } 65 | 66 | signature,_, reCode := owcrypt.Signature(prikey, nil, hashBytes, owcrypt.ECC_CURVE_SECP256K1) 67 | if reCode != owcrypt.SUCCESS { 68 | return "", errors.New("Failed to sign transaction!") 69 | } 70 | 71 | serilizeS(signature) 72 | 73 | return hex.EncodeToString(signature), nil 74 | } 75 | 76 | func VerifyAndCombineTransaction(emptyTrans string, inputs []*TxInput) (bool, string) { 77 | 78 | for _, input := range inputs { 79 | if !verifyTransactionHash(input.pubkey, input.hash, input.signature) { 80 | return false, "" 81 | } 82 | } 83 | 84 | tx := getSubmitBytes(inputs, emptyTrans) 85 | 86 | if tx == nil { 87 | return false, "" 88 | } 89 | 90 | return true, hex.EncodeToString(tx) 91 | } -------------------------------------------------------------------------------- /mateverseTransaction/txHash.go: -------------------------------------------------------------------------------- 1 | package mateverseTransaction 2 | 3 | -------------------------------------------------------------------------------- /mateverseTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package mateverseTransaction 2 | 3 | const ( 4 | DefaultTxVersion = uint32(0x04) 5 | SigHashAll = uint32(0x01) 6 | OpCheckAttenuationVerify = "b2" 7 | OpDup = "76" 8 | OpHash160 = "a9" 9 | OpEqualVerify = "88" 10 | OpCheckSig = "ac" 11 | ) 12 | 13 | 14 | var ( 15 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 16 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 17 | ) -------------------------------------------------------------------------------- /mateverseTransaction/txSigPub.go: -------------------------------------------------------------------------------- 1 | package mateverseTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "github.com/blocktree/go-owcrypt" 6 | ) 7 | 8 | type SignaturePubkey struct { 9 | Signature []byte 10 | Pubkey []byte 11 | } 12 | 13 | func (sp SignaturePubkey) encodeSignatureToScript(sigType byte) []byte { 14 | r := sp.Signature[:32] 15 | s := sp.Signature[32:] 16 | if r[0]&0x80 == 0x80 { 17 | r = append([]byte{0x00}, r...) 18 | } else { 19 | for i := 0; i < 32; i++ { 20 | if r[0] == 0 && r[1]&0x80 != 0x80 { 21 | r = r[1:] 22 | } else { 23 | break 24 | } 25 | } 26 | } 27 | if s[0]&0x80 == 0x80 { 28 | s = append([]byte{0}, s...) 29 | } else { 30 | for i := 0; i < 32; i++ { 31 | if s[0] == 0 && s[1]&0x80 != 0x80 { 32 | s = s[1:] 33 | } else { 34 | break 35 | } 36 | } 37 | } 38 | 39 | r = append([]byte{byte(len(r))}, r...) 40 | r = append([]byte{0x02}, r...) 41 | s = append([]byte{byte(len(s))}, s...) 42 | s = append([]byte{0x02}, s...) 43 | 44 | rs := append(r, s...) 45 | rs = append([]byte{byte(len(rs))}, rs...) 46 | rs = append(rs, sigType) 47 | rs = append([]byte{0x30}, rs...) 48 | rs = append([]byte{byte(len(rs))}, rs...) 49 | 50 | return rs 51 | } 52 | func (sp SignaturePubkey) encodeToScript(sigType byte) []byte { 53 | 54 | ret := append(sp.encodeSignatureToScript(sigType), append([]byte{byte(len(sp.Pubkey))}, sp.Pubkey...)...) 55 | return append([]byte{byte(len(ret))}, ret...) 56 | } 57 | 58 | 59 | func verifyTransactionHash(pubkey, hash, signature string) bool { 60 | pubBytes, err := hex.DecodeString(pubkey) 61 | if err != nil || len(pubBytes) != 33 { 62 | return false 63 | } 64 | 65 | hashBytes, err := hex.DecodeString(hash) 66 | if err != nil || len(hashBytes) != 32 { 67 | return false 68 | } 69 | 70 | sigBytes, err := hex.DecodeString(signature) 71 | if err != nil || len(sigBytes) != 64 { 72 | return false 73 | } 74 | 75 | pubBytes = owcrypt.PointDecompress(pubBytes, owcrypt.ECC_CURVE_SECP256K1)[1:] 76 | if owcrypt.SUCCESS != owcrypt.Verify(pubBytes, nil, hashBytes, sigBytes, owcrypt.ECC_CURVE_SECP256K1) { 77 | return false 78 | } 79 | 80 | return true 81 | } 82 | -------------------------------------------------------------------------------- /mateverseTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package mateverseTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | "math/big" 8 | ) 9 | 10 | func byteArrayCompare(a, b []byte) bool { 11 | if len(a) != len(b) { 12 | return false 13 | } 14 | for index := 0; index < len(a); index++ { 15 | if a[index] != b[index] { 16 | return false 17 | } 18 | } 19 | return true 20 | } 21 | 22 | //reverseBytes endian reverse 23 | func reverseBytes(s []byte) []byte { 24 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 25 | s[i], s[j] = s[j], s[i] 26 | } 27 | return s 28 | } 29 | 30 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 31 | func reverseHexToBytes(hexVar string) ([]byte, error) { 32 | if len(hexVar)%2 == 1 { 33 | return nil, errors.New("Invalid TxHash!") 34 | } 35 | ret, err := hex.DecodeString(hexVar) 36 | if err != nil { 37 | return nil, err 38 | } 39 | return reverseBytes(ret), nil 40 | } 41 | 42 | //reverseBytesToHex change the endian of the input byte array then encode it to hex string 43 | func reverseBytesToHex(bytesVar []byte) string { 44 | return hex.EncodeToString(reverseBytes(bytesVar)) 45 | } 46 | 47 | //uint16ToLittleEndianBytes 48 | func uint16ToLittleEndianBytes(data uint16) []byte { 49 | tmp := [2]byte{} 50 | binary.LittleEndian.PutUint16(tmp[:], data) 51 | return tmp[:] 52 | } 53 | 54 | //littleEndianBytesToUint16 55 | func littleEndianBytesToUint16(data []byte) uint16 { 56 | return binary.LittleEndian.Uint16(data) 57 | } 58 | 59 | //uint32ToLittleEndianBytes 60 | func uint32ToLittleEndianBytes(data uint32) []byte { 61 | tmp := [4]byte{} 62 | binary.LittleEndian.PutUint32(tmp[:], data) 63 | return tmp[:] 64 | } 65 | 66 | //littleEndianBytesToUint32 67 | func littleEndianBytesToUint32(data []byte) uint32 { 68 | return binary.LittleEndian.Uint32(data) 69 | } 70 | 71 | //uint64ToLittleEndianBytes 72 | func uint64ToLittleEndianBytes(data uint64) []byte { 73 | tmp := [8]byte{} 74 | binary.LittleEndian.PutUint64(tmp[:], data) 75 | return tmp[:] 76 | } 77 | 78 | //littleEndianBytesToUint64 79 | func littleEndianBytesToUint64(data []byte) uint64 { 80 | return binary.LittleEndian.Uint64(data) 81 | } 82 | 83 | func serilizeS(sig []byte) []byte { 84 | s := sig[32:] 85 | numS := new(big.Int).SetBytes(s) 86 | numHalfOrder := new(big.Int).SetBytes(HalfCurveOrder) 87 | if numS.Cmp(numHalfOrder) > 0 { 88 | numOrder := new(big.Int).SetBytes(CurveOrder) 89 | numS.Sub(numOrder, numS) 90 | 91 | s = numS.Bytes() 92 | if len(s) < 32 { 93 | for i := 0; i < 32-len(s); i++ { 94 | s = append([]byte{0x00}, s...) 95 | } 96 | } 97 | return append(sig[:32], s...) 98 | } 99 | return sig 100 | } -------------------------------------------------------------------------------- /moacchainTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package moacchainTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "math/big" 7 | "strings" 8 | 9 | owcrypt "github.com/blocktree/go-owcrypt" 10 | ) 11 | 12 | func CreateEmptyRawTransactionAndHash(to string, nonce uint64, amount, gasLimit, gasPrice *big.Int, isTestNet bool) (string, string, error) { 13 | tx, err := NewTxStruct(to, nonce, amount, gasLimit, gasPrice, nil, isTestNet) 14 | if err != nil { 15 | return "", "", err 16 | } 17 | emptyTrans := tx.ToBytes() 18 | hash := owcrypt.Hash(emptyTrans, 0, owcrypt.HASH_ALG_KECCAK256) 19 | 20 | return hex.EncodeToString(emptyTrans), hex.EncodeToString(hash), nil 21 | } 22 | 23 | func SignRawTransaction(hash string, privateKey []byte) ([]byte, error) { 24 | hashBytes, err := hex.DecodeString(hash) 25 | if err != nil || len(hashBytes) != 32 { 26 | return nil, errors.New("Invalid hash string!") 27 | } 28 | signature, v, retCode := owcrypt.Signature(privateKey, nil, hashBytes, owcrypt.ECC_CURVE_SECP256K1) 29 | if retCode != owcrypt.SUCCESS { 30 | return nil, errors.New("Failed to sign transaction!") 31 | } 32 | return append(signature, v), nil 33 | } 34 | 35 | func VerifyAndCombineRawTransaction(emptyTrans, signature, publicKey string, isTestNet bool) (bool, string) { 36 | txBytes, err := hex.DecodeString(emptyTrans) 37 | if err != nil { 38 | return false, "" 39 | } 40 | hash := owcrypt.Hash(txBytes, 0, owcrypt.HASH_ALG_KECCAK256) 41 | sig, err := hex.DecodeString(signature) 42 | if err != nil || len(sig) != 65 || (sig[64] != 0x01) && sig[64] != 0x00 { 43 | return false, "" 44 | } 45 | pub, retCode := owcrypt.RecoverPubkey(sig, hash, owcrypt.ECC_CURVE_SECP256K1) 46 | if retCode != owcrypt.SUCCESS || strings.ToLower(hex.EncodeToString(owcrypt.PointCompress(pub, owcrypt.ECC_CURVE_SECP256K1))) != strings.ToLower(publicKey) { 47 | return false, "" 48 | } 49 | if owcrypt.SUCCESS != owcrypt.Verify(pub, nil, hash, sig[:64], owcrypt.ECC_CURVE_SECP256K1) { 50 | return false, "" 51 | } 52 | if len(txBytes) < 57 { 53 | head := getHeadBytes(SmallTag, LargeTag, uint64(len(txBytes)-1)) 54 | if head[0] != txBytes[0] { 55 | return false, "" 56 | } 57 | } else { 58 | // 59 | } 60 | tx, err := decodeEmpty(txBytes[1:]) 61 | if err != nil { 62 | return false, "" 63 | } 64 | tx.addSig(sig, isTestNet) 65 | 66 | return true, "0x" + hex.EncodeToString(tx.ToBytes()) 67 | } 68 | -------------------------------------------------------------------------------- /moacchainTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package moacchainTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "math/big" 7 | "testing" 8 | ) 9 | 10 | func Test_transaction(t *testing.T) { 11 | to := "0xeb916695b6d6a8f9c18f344754f13b7b11d62268" 12 | nonce := uint64(1) 13 | gasPrice := big.NewInt(6000) 14 | gasLimit := big.NewInt(200) 15 | amount := big.NewInt(40000) 16 | 17 | emptyTrans, hash, err := CreateEmptyRawTransactionAndHash(to, nonce, amount, gasLimit, gasPrice, false) 18 | if err != nil { 19 | t.Error(err) 20 | } else { 21 | fmt.Println("空交易单 : \n", emptyTrans) 22 | fmt.Println("哈希 : \n", hash) 23 | } 24 | 25 | privkey, _ := hex.DecodeString("483ada7726a3c4655da4fbfc0e1108a8fd17b448a68554199c47d08ffb10d4b8") 26 | sig, err := SignRawTransaction(hash, privkey) 27 | if err != nil { 28 | t.Error(err) 29 | } else { 30 | fmt.Println("sig : \n", hex.EncodeToString(sig)) 31 | } 32 | 33 | pubkey := "034EE1C207899C24D00B1B918C42F798C8BE8A46AD22DE0B57F6774C026C7D11AB" 34 | 35 | pass, signedTrans := VerifyAndCombineRawTransaction(emptyTrans, hex.EncodeToString(sig), pubkey, false) 36 | if pass { 37 | fmt.Println("合并之后的交易单 : \n", signedTrans) 38 | } else { 39 | t.Error("Verify falid !") 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /moacchainTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package moacchainTransaction 2 | 3 | const ( 4 | SmallTag byte = 0xC0 5 | LargeTag byte = 0xF7 6 | ChainIDMainnet byte = 99 7 | ChainIDTestnet byte = 101 8 | ) 9 | -------------------------------------------------------------------------------- /moacchainTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package moacchainTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | "math/big" 8 | ) 9 | 10 | func getLengthBytes(len int) []byte { 11 | return []byte{byte(len + 0x80)} 12 | } 13 | 14 | func getUint64Bytes(data uint64) []byte { 15 | if data == 0x00 { 16 | return []byte{0x80} 17 | } else if data < 0x80 { 18 | return []byte{byte(data)} 19 | } 20 | 21 | bytes := make([]byte, 8) 22 | binary.BigEndian.PutUint64(bytes, data) 23 | index := 0 24 | for _, b := range bytes { 25 | if b == 0 { 26 | index++ 27 | } else { 28 | break 29 | } 30 | } 31 | 32 | return append(getLengthBytes(8-index), bytes[index:]...) 33 | } 34 | 35 | func getBigIntBytes(data *big.Int) []byte { 36 | ret := data.Bytes() 37 | 38 | return append(getLengthBytes(len(ret)), ret...) 39 | } 40 | 41 | func getHeadBytes(smalTag, largeTag byte, size uint64) []byte { 42 | if size < 56 { 43 | return []byte{smalTag + byte(size)} 44 | } 45 | ret := make([]byte, 9) 46 | sizesize := putint(ret[1:], size) 47 | ret[0] = largeTag + byte(sizesize) 48 | return ret[:sizesize+1] 49 | } 50 | 51 | func putint(b []byte, i uint64) (size int) { 52 | switch { 53 | case i < (1 << 8): 54 | b[0] = byte(i) 55 | return 1 56 | case i < (1 << 16): 57 | b[0] = byte(i >> 8) 58 | b[1] = byte(i) 59 | return 2 60 | case i < (1 << 24): 61 | b[0] = byte(i >> 16) 62 | b[1] = byte(i >> 8) 63 | b[2] = byte(i) 64 | return 3 65 | case i < (1 << 32): 66 | b[0] = byte(i >> 24) 67 | b[1] = byte(i >> 16) 68 | b[2] = byte(i >> 8) 69 | b[3] = byte(i) 70 | return 4 71 | case i < (1 << 40): 72 | b[0] = byte(i >> 32) 73 | b[1] = byte(i >> 24) 74 | b[2] = byte(i >> 16) 75 | b[3] = byte(i >> 8) 76 | b[4] = byte(i) 77 | return 5 78 | case i < (1 << 48): 79 | b[0] = byte(i >> 40) 80 | b[1] = byte(i >> 32) 81 | b[2] = byte(i >> 24) 82 | b[3] = byte(i >> 16) 83 | b[4] = byte(i >> 8) 84 | b[5] = byte(i) 85 | return 6 86 | case i < (1 << 56): 87 | b[0] = byte(i >> 48) 88 | b[1] = byte(i >> 40) 89 | b[2] = byte(i >> 32) 90 | b[3] = byte(i >> 24) 91 | b[4] = byte(i >> 16) 92 | b[5] = byte(i >> 8) 93 | b[6] = byte(i) 94 | return 7 95 | default: 96 | b[0] = byte(i >> 56) 97 | b[1] = byte(i >> 48) 98 | b[2] = byte(i >> 40) 99 | b[3] = byte(i >> 32) 100 | b[4] = byte(i >> 24) 101 | b[5] = byte(i >> 16) 102 | b[6] = byte(i >> 8) 103 | b[7] = byte(i) 104 | return 8 105 | } 106 | } 107 | 108 | func getAddressHashBytes(address string) ([]byte, error) { 109 | if address[:2] != "0x" { 110 | return nil, errors.New("Invalid address!") 111 | } 112 | hash, err := hex.DecodeString(address[2:]) 113 | if err != nil || len(hash) != 20 { 114 | return nil, errors.New("Invalid address!") 115 | } 116 | 117 | return append(getLengthBytes(20), hash...), nil 118 | } 119 | -------------------------------------------------------------------------------- /omniTransaction/txOmni.go: -------------------------------------------------------------------------------- 1 | package omniTransaction 2 | 3 | type OmniStruct struct { 4 | TxType int 5 | PropertyId uint32 6 | Amount uint64 7 | Ecosystem byte 8 | Memo string 9 | Address string 10 | } 11 | 12 | func (os OmniStruct) getPayload() []byte { 13 | payload := []byte{} 14 | switch os.TxType { 15 | case SimpleSend: 16 | payload = createPayloadSimpleSend(os.PropertyId, os.Amount) 17 | break 18 | case SendAll: 19 | payload = createPayloadSendAll(os.Ecosystem) 20 | break 21 | case DExAccept: 22 | payload = createPayloadDExAccept(os.PropertyId, os.Amount) 23 | break 24 | case MetaDExCancelEcosystem: 25 | payload = createPayloadMetaDExCancelEcosystem(os.Ecosystem) 26 | break 27 | case CloseCrowdsale: 28 | payload = createPayloadCloseCrowdsale(os.PropertyId) 29 | break 30 | case Grant: 31 | payload = createPayloadGrant(os.PropertyId, os.Amount, os.Memo) 32 | break 33 | case Revoke: 34 | payload = createPayloadRevoke(os.PropertyId, os.Amount, os.Memo) 35 | break 36 | case ChangeIssuer: 37 | payload = createPayloadChangeIssuer(os.PropertyId) 38 | break 39 | case EnableFreezing: 40 | payload = createPayloadEnableFreezing(os.PropertyId) 41 | break 42 | case DisableFreezing: 43 | payload = createPayloadDisableFreezing(os.PropertyId) 44 | break 45 | case FreezeTokens: 46 | payload = createPayloadFreezeTokens(os.PropertyId, os.Amount, os.Address) 47 | break 48 | case UnfreezeTokens: 49 | payload = createPayloadUnfreezeTokens(os.PropertyId, os.Amount, os.Address) 50 | break 51 | default: 52 | return nil 53 | } 54 | 55 | payload = append(OmniPrefix[:], payload...) 56 | payload = append([]byte{OpReturn, byte(len(payload))}, payload...) 57 | return payload 58 | } 59 | -------------------------------------------------------------------------------- /omniTransaction/txOut.go: -------------------------------------------------------------------------------- 1 | package omniTransaction 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | type TxOut struct { 9 | amount []byte 10 | lockScript []byte 11 | } 12 | 13 | func newTxOutForEmptyTrans(vout []Vout, omniDetail OmniStruct, addressPrefix AddressPrefix) ([]TxOut, error) { 14 | if vout == nil || len(vout) == 0 { 15 | return nil, errors.New("No address to send when create an empty transaction!") 16 | } 17 | var ret []TxOut 18 | var prefixStr string 19 | var p2pkhPrefixByte []byte 20 | var p2wpkhPrefixByte []byte 21 | prefixStr = addressPrefix.Bech32Prefix 22 | p2pkhPrefixByte = addressPrefix.P2PKHPrefix 23 | p2wpkhPrefixByte = addressPrefix.P2WPKHPrefix 24 | 25 | for _, v := range vout { 26 | amount := uint64ToLittleEndianBytes(v.Amount) 27 | 28 | if strings.Index(v.Address, prefixStr) == 0 { 29 | redeem, err := Bech32Decode(v.Address) 30 | if err != nil { 31 | return nil, errors.New("Invalid bech32 type address!") 32 | } 33 | 34 | redeem = append([]byte{byte(len(redeem))}, redeem...) 35 | redeem = append([]byte{0x00}, redeem...) 36 | 37 | ret = append(ret, TxOut{amount, redeem}) 38 | } 39 | 40 | prefix, hash, err := DecodeCheck(v.Address) 41 | if err != nil { 42 | return nil, errors.New("Invalid address to send!") 43 | } 44 | 45 | if len(hash) != 0x14 { 46 | return nil, errors.New("Invalid address to send!") 47 | } 48 | 49 | hash = append([]byte{byte(len(hash))}, hash...) 50 | hash = append([]byte{OpCodeHash160}, hash...) 51 | if byteArrayCompare(prefix, p2pkhPrefixByte) { 52 | hash = append(hash, OpCodeEqualVerify, OpCodeCheckSig) 53 | hash = append([]byte{OpCodeDup}, hash...) 54 | } else if byteArrayCompare(prefix, p2wpkhPrefixByte) { 55 | hash = append(hash, OpCodeEqual) 56 | } else { 57 | return nil, errors.New("Invalid address to send!") 58 | } 59 | 60 | ret = append(ret, TxOut{amount, hash}) 61 | } 62 | 63 | ret = append(ret, TxOut{uint64ToLittleEndianBytes(0), omniDetail.getPayload()}) 64 | 65 | return ret, nil 66 | } 67 | 68 | func (out TxOut) toBytes() ([]byte, error) { 69 | if out.amount == nil || len(out.amount) != 8 { 70 | return nil, errors.New("Invalid amount for a transaction output!") 71 | } 72 | if out.lockScript == nil || len(out.lockScript) == 0 { 73 | return nil, errors.New("Invalid lock script for a transaction output!") 74 | } 75 | 76 | ret := []byte{} 77 | ret = append(ret, out.amount...) 78 | ret = append(ret, byte(len(out.lockScript))) 79 | ret = append(ret, out.lockScript...) 80 | 81 | return ret, nil 82 | } 83 | -------------------------------------------------------------------------------- /omniTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package omniTransaction 2 | 3 | type AddressPrefix struct { 4 | P2PKHPrefix []byte 5 | P2WPKHPrefix []byte 6 | Bech32Prefix string 7 | } 8 | 9 | var ( 10 | BTCMainnetAddressPrefix = AddressPrefix{[]byte{0x00}, []byte{0x05}, "bc"} 11 | BTCTestnetAddressPrefix = AddressPrefix{[]byte{0x6F}, []byte{0xC4}, "tb"} 12 | ) 13 | 14 | // Omni transaction type 15 | const ( 16 | SimpleSend = 0 17 | SendAll = 4 18 | DExAccept = 22 19 | MetaDExCancelEcosystem = 28 20 | CloseCrowdsale = 53 21 | Grant = 55 22 | Revoke = 56 23 | ChangeIssuer = 70 24 | EnableFreezing = 71 25 | DisableFreezing = 72 26 | FreezeTokens = 185 27 | UnfreezeTokens = 186 28 | ) 29 | 30 | // propertyID for USDT 31 | const ( 32 | MainTetherUS_01 = uint32(31) 33 | MainTetherUS_02 = uint32(192) 34 | MainTetherUS_03 = uint32(330) 35 | MainTetherUS_04 = uint32(341) 36 | MainTetherUS_05 = uint32(396) 37 | MainTetherUS_06 = uint32(397) 38 | MainTetherUS_07 = uint32(398) 39 | MainTetherUS_08 = uint32(399) 40 | MainTetherUS_09 = uint32(404) 41 | 42 | TestTetherUS_01 = uint32(2147484026) 43 | TestTetherUS_02 = uint32(2147484061) 44 | TestTetherUS_03 = uint32(2147484062) 45 | 46 | DefaultTetherUSID = TestTetherUS_01 47 | ) 48 | 49 | // ecosystem defination for send all payload 50 | const ( 51 | EcoSystemMain = byte(1) 52 | EcoSystemTest = byte(2) 53 | 54 | DefaultEcoSystem = EcoSystemTest 55 | ) 56 | 57 | const ( 58 | DefaultTxVersion = uint32(2) 59 | DefaultHashType = uint32(1) 60 | MaxScriptElementSize = 520 61 | ) 62 | 63 | const ( 64 | SequenceFinal = uint32(0xFFFFFFFF) 65 | SequenceMaxBip125RBF = uint32(0xFFFFFFFD) 66 | ) 67 | 68 | const ( 69 | SegWitSymbol = byte(0) 70 | SegWitVersion = byte(1) 71 | SigHashAll = byte(1) 72 | ) 73 | 74 | const ( 75 | OpCodeHash160 = byte(0xA9) 76 | OpCodeEqual = byte(0x87) 77 | OpCodeEqualVerify = byte(0x88) 78 | OpCodeCheckSig = byte(0xAC) 79 | OpCodeDup = byte(0x76) 80 | OpCode_1 = byte(0x51) 81 | OpCheckMultiSig = byte(0xAE) 82 | OpPushData1 = byte(0x4C) 83 | OpPushData2 = byte(0x4D) 84 | OpPushData3 = byte(0x4E) 85 | OpReturn = byte(0x6A) 86 | ) 87 | 88 | var ( 89 | OmniPrefix = [4]byte{0x6F, 0x6D, 0x6E, 0x69} 90 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 91 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 92 | ) 93 | -------------------------------------------------------------------------------- /ontologyTransaction/README.md: -------------------------------------------------------------------------------- 1 | # Ontology Transaction Driver 2 | ``` 3 | transaction_test.go测试案例说明 4 | Test_case1 : ONT转账 5 | Test_case2 : ONG转账 6 | Test_case3 : 已解绑ONG提取 7 | ``` 8 | ## 当前支持 9 | ``` 10 | 交易单构建 11 | 交易单签名 12 | 交易单合并 13 | 交易单验签 14 | ``` 15 | ## TODO 16 | ``` 17 | 多签 18 | ``` -------------------------------------------------------------------------------- /ontologyTransaction/txHash.go: -------------------------------------------------------------------------------- 1 | package ontologyTransaction 2 | 3 | type TxHash struct { 4 | Hash string 5 | Addresses []string 6 | } 7 | 8 | func (tx TxHash) GetTxHashHex() string { 9 | return tx.Hash 10 | } 11 | -------------------------------------------------------------------------------- /ontologyTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package ontologyTransaction 2 | 3 | const AddressPrefix = byte(0x17) 4 | 5 | const ( 6 | AssetONT = 0 7 | AssetONG = 1 8 | AssetONGWithdraw = 2 9 | ) 10 | 11 | const ( 12 | DefaultGasPrice = uint64(500) 13 | DefaultGasLimit = uint64(20000) 14 | ) 15 | 16 | const ( 17 | ONTContractVersion = byte(0x00) 18 | ONGContractVersion = byte(0x00) 19 | ONTContractAddress = "0100000000000000000000000000000000000000" 20 | ONGContractAddress = "0200000000000000000000000000000000000000" 21 | TxTypeInvoke = byte(0xD1) 22 | DefaultAttribute = byte(0) 23 | ) 24 | 25 | const ( 26 | PushBytes75 = 0x4B 27 | PushData1 = 0x4C 28 | PushData2 = 0x4D 29 | PushData4 = 0x4E 30 | ) 31 | 32 | const ( 33 | MethodTransfer = "transfer" 34 | MethodTransferV2 = "transferV2" 35 | MethodTransferFrom = "transferFrom" 36 | NativeInvokeName = "Ontology.Native.Invoke" 37 | ) 38 | 39 | const ( 40 | OpCodePushM1 = byte(0x4F) 41 | OpCodeNewStruct = byte(0xC6) 42 | OpCodeToALTStack = byte(0x6B) 43 | OpCodeDupFromALTStack = byte(0x6A) 44 | OpCodeAppend = byte(0xC8) 45 | OpCodePush0 = byte(0x00) 46 | OpCodePush1 = byte(0x51) 47 | OpCodeFromALTStack = byte(0x6C) 48 | OpCodePack = byte(0xC1) 49 | OpCodeSysCall = byte(0x68) 50 | OpCodeCheckSig = byte(0xAC) 51 | OpCodeCheckMultiSig = byte(0xAE) 52 | ) 53 | 54 | var ( 55 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51} 56 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0x80, 0x00, 0x00, 0x00, 0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xDE, 0x73, 0x7D, 0x56, 0xD3, 0x8B, 0xCF, 0x42, 0x79, 0xDC, 0xE5, 0x61, 0x7E, 0x31, 0x92, 0xA8} 57 | ) 58 | -------------------------------------------------------------------------------- /ontologyTransaction/txSigData.go: -------------------------------------------------------------------------------- 1 | package ontologyTransaction 2 | 3 | type SigData struct { 4 | Nrequired uint16 5 | SigPubs []SigPub 6 | } 7 | -------------------------------------------------------------------------------- /owkeychain/hdprofile.go: -------------------------------------------------------------------------------- 1 | package owkeychain 2 | 3 | import ( 4 | "encoding/hex" 5 | "github.com/blocktree/go-owcrypt" 6 | ) 7 | 8 | var ( 9 | openwalletPrePath = "m/44'/88'" 10 | ) 11 | 12 | type CoinType struct { 13 | hdIndex uint32 14 | curveType uint32 15 | } 16 | 17 | //XXX[0]:hd扩展索引 18 | //XXX[1]:曲线类型 19 | var ( 20 | Bitcoin = CoinType{uint32(0), owcrypt.ECC_CURVE_SECP256K1} 21 | Ethereum = CoinType{uint32(1), owcrypt.ECC_CURVE_SECP256K1} 22 | ) 23 | 24 | var ( 25 | owprvPrefix = []byte{0x07, 0xa8, 0x10, 0x0c, 0x28} 26 | owpubPrefix = []byte{0x07, 0xa8, 0x10, 0x31, 0xa2} 27 | owpubPrefix_BLS = []byte{0x3e, 0x00, 0xfa, 0xea, 0x69} 28 | 29 | BitcoinPubkeyPrefix = []byte{0} 30 | BitcoinScriptPrefix = []byte{5} 31 | ) 32 | 33 | var ( 34 | curveorder_secp256k1 = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 35 | curveorder_secp256r1 = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xBC, 0xE6, 0xFA, 0xAD, 0xA7, 0x17, 0x9E, 0x84, 0xF3, 0xB9, 0xCA, 0xC2, 0xFC, 0x63, 0x25, 0x51} 36 | curveorder_sm2_std = []byte{0xFF, 0xFF, 0xFF, 0xFE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x72, 0x03, 0xDF, 0x6B, 0x21, 0xC6, 0x05, 0x2B, 0x53, 0xBB, 0xF4, 0x09, 0x39, 0xD5, 0x41, 0x23} 37 | curveorder_ed25519 = []byte{0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x14, 0xDE, 0xF9, 0xDE, 0xA2, 0xF7, 0x9C, 0xD6, 0x58, 0x12, 0x63, 0x1A, 0x5C, 0xF5, 0xD3, 0xED} 38 | curveoeder_bls12_381 = []byte{0x73, 0xed, 0xa7, 0x53, 0x29, 0x9d, 0x7d, 0x48, 0x33, 0x39, 0xd8, 0x08, 0x09, 0xa1, 0xd8, 0x05, 0x53, 0xbd, 0xa4, 0x02, 0xff, 0xfe, 0x5b, 0xfe, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x01} 39 | ) 40 | 41 | func getCurveOrder(typeChoose uint32) []byte { 42 | ret := make([]byte, 32) 43 | switch typeChoose { 44 | case owcrypt.ECC_CURVE_SECP256K1: 45 | copy(ret, curveorder_secp256k1) 46 | break 47 | case owcrypt.ECC_CURVE_SECP256R1: 48 | copy(ret, curveorder_secp256r1) 49 | break 50 | case owcrypt.ECC_CURVE_SM2_STANDARD: 51 | copy(ret, curveorder_sm2_std) 52 | break 53 | case owcrypt.ECC_CURVE_ED25519, owcrypt.ECC_CURVE_ED25519_NORMAL, owcrypt.ECC_CURVE_X25519, owcrypt.ECC_CURVE_CURVE25519_SHA256: 54 | copy(ret, curveorder_ed25519) 55 | break 56 | case owcrypt.ECC_CURVE_BLS12381_G2_XMD_SHA_256_SSWU_RO_NUL, owcrypt.ECC_CURVE_BLS12381_G2_XMD_SHA_256_SSWU_RO_AUG: 57 | copy(ret, curveoeder_bls12_381) 58 | break 59 | case owcrypt.ECC_CURVE_PASTA: 60 | //ret = owcrypt.GetCurveOrder(owcrypt.ECC_CURVE_PASTA) 61 | ret, _ = hex.DecodeString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") 62 | break 63 | default: 64 | return nil 65 | break 66 | } 67 | return ret 68 | } 69 | -------------------------------------------------------------------------------- /owkeychain/multisig.go: -------------------------------------------------------------------------------- 1 | package owkeychain 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | var ChainPrefix = []byte{0x08, 0x61, 0x24, 0x96, 0x12, 0xae, 0x5c} 9 | 10 | type MultiSigShare struct { 11 | Pubkey []byte 12 | ChainCode []byte 13 | CurveType uint32 14 | } 15 | 16 | func (ms MultiSigShare) ChianEncode() string { 17 | data := make([]byte, 0) 18 | data = append(data, ChainPrefix...) 19 | if len(ms.Pubkey) == 32 { 20 | data = append(data, 0x00) 21 | } 22 | 23 | data = append(data, ms.Pubkey...) 24 | data = append(data, ms.ChainCode...) 25 | data = append(data, uint32ToBytes(ms.CurveType)...) 26 | 27 | return Encode(data, BitcoinAlphabet) 28 | } 29 | 30 | func ChainDecode(owchain string) (*MultiSigShare, error) { 31 | if strings.Index(owchain, "owchain") != 0 { 32 | return nil, errors.New("Invalid owchain encoded data!") 33 | } 34 | 35 | chainBytes, err := Decode(owchain, BitcoinAlphabet) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | if len(chainBytes) != 76 { 41 | return nil, errors.New("Invalid owchain encoded data length!") 42 | } 43 | 44 | for i, v := range ChainPrefix { 45 | if v != chainBytes[i] { 46 | return nil, errors.New("Invalid owchian prefix data!") 47 | } 48 | } 49 | pubkey := chainBytes[7:40] 50 | if pubkey[0] == 0x00 { 51 | pubkey = pubkey[1:] 52 | } 53 | return &MultiSigShare{ 54 | Pubkey: pubkey, 55 | ChainCode: chainBytes[40:72], 56 | CurveType: bytesToUInt32(chainBytes[72:]), 57 | }, nil 58 | } 59 | 60 | func GetMultiSigShareData(owpub string) (string, error) { 61 | pubParent, err := OWDecode(owpub) 62 | if err != nil { 63 | return "", err 64 | } 65 | 66 | if pubParent.isPrivate { 67 | pubParent = pubParent.GetPublicKey() 68 | } 69 | 70 | var ms MultiSigShare 71 | 72 | ms.Pubkey = pubParent.key 73 | ms.ChainCode = pubParent.chainCode 74 | ms.CurveType = pubParent.curveType 75 | 76 | return ms.ChianEncode(), nil 77 | } 78 | -------------------------------------------------------------------------------- /owkeychain/multisig_test.go: -------------------------------------------------------------------------------- 1 | package owkeychain 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func Test_chainencode(t *testing.T) { 9 | owpub := "owpubeyoV6FsQ8AAx5VjEQvV1FyQjkEhf3aP3EifHiDok2wv53GHeUq12tUAFvyMPrzrJA5tvVYDdsjgTrXDSuo4poZXrtHACCsLq3NegLGtvbq27VkNKB" 10 | 11 | extendKey, err := OWDecode(owpub) 12 | if err != nil { 13 | t.Error(err) 14 | } 15 | 16 | owchain, err := GetMultiSigShareData(owpub) 17 | if err != nil { 18 | t.Error(err) 19 | } else { 20 | fmt.Println(owchain) 21 | } 22 | 23 | ms, err := ChainDecode(owchain) 24 | if err != nil { 25 | t.Error(err) 26 | } 27 | 28 | for index := 0; index < len(ms.Pubkey); index++ { 29 | if ms.Pubkey[index] != extendKey.key[index] { 30 | t.Error("decode key failed!") 31 | } 32 | } 33 | 34 | for index := 0; index < 32; index++ { 35 | if ms.ChainCode[index] != extendKey.chainCode[index] { 36 | t.Error("decode chaincode failed!") 37 | } 38 | } 39 | 40 | if ms.CurveType != extendKey.curveType { 41 | t.Error("decode curvetype failed!") 42 | } 43 | 44 | chk := ms.ChianEncode() 45 | 46 | if chk != owchain { 47 | t.Error("encode failed!") 48 | } else { 49 | fmt.Println("success!") 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /polkadotTransaction/codec_test.go: -------------------------------------------------------------------------------- 1 | package polkadotTransaction 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "testing" 7 | ) 8 | 9 | func Test_Encode(t *testing.T) { 10 | testTable := map[uint64]string { 11 | 1: "04", 12 | 2: "08", 13 | 63: "fc", 14 | 64: "0101", 15 | 128: "0102", 16 | 192: "0103", 17 | 256: "0104", 18 | 320: "0105", 19 | 512: "0108", 20 | 16383: "fdff", 21 | 16384: "02000100", 22 | 1073741823: "feffffff", 23 | 1073741824: "0300000040", 24 | 4102610000: "0350dc88f4", 25 | 14102610000: "0750c0944803", 26 | } 27 | 28 | for i, excepted := range testTable { 29 | if excepted != Encode(i) { 30 | t.Error(i, " failed") 31 | } 32 | } 33 | } 34 | 35 | func TestBytesToCompactBytes(t *testing.T) { 36 | a := big.NewInt(14102610000) 37 | fmt.Println(a.BitLen()) 38 | a.Bytes() 39 | 40 | } -------------------------------------------------------------------------------- /polkadotTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package polkadotTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "github.com/blocktree/go-owcrypt" 7 | ) 8 | 9 | func (ts TxStruct) CreateEmptyTransactionAndMessage(transferCode string) (string, string, error) { 10 | 11 | tp, err := ts.NewTxPayLoad(transferCode) 12 | if err != nil { 13 | return "", "", err 14 | } 15 | 16 | return ts.ToJSONString(), tp.ToBytesString(), nil 17 | } 18 | 19 | func SignTransaction(msgStr string, prikey []byte) ([]byte, error) { 20 | msg, err := hex.DecodeString(msgStr) 21 | if err != nil || len(msg) == 0 { 22 | return nil, errors.New("invalid message to sign") 23 | } 24 | 25 | if prikey == nil || len(prikey) != 32 { 26 | return nil, errors.New("invalid private key") 27 | } 28 | 29 | signature, _, retCode := owcrypt.Signature(prikey, nil, msg, owcrypt.ECC_CURVE_ED25519) 30 | if retCode != owcrypt.SUCCESS { 31 | return nil, errors.New("sign failed") 32 | } 33 | 34 | return signature, nil 35 | } 36 | 37 | func VerifyAndCombineTransaction(transferCode, emptyTrans, signature string) (string, bool) { 38 | ts, err := NewTxStructFromJSON(emptyTrans) 39 | if err != nil { 40 | return "", false 41 | } 42 | 43 | tp, err := ts.NewTxPayLoad(transferCode) 44 | if err != nil { 45 | return "", false 46 | } 47 | 48 | msg, _ := hex.DecodeString(tp.ToBytesString()) 49 | 50 | pubkey, _ := hex.DecodeString(ts.SenderPubkey) 51 | 52 | sig, err := hex.DecodeString(signature) 53 | if err != nil || len(sig) != 64{ 54 | return "", false 55 | } 56 | 57 | if owcrypt.SUCCESS != owcrypt.Verify(pubkey, nil, msg, sig, owcrypt.ECC_CURVE_ED25519) { 58 | return "", false 59 | } 60 | 61 | signned, err := ts.GetSignedTransaction(transferCode, signature) 62 | if err != nil { 63 | return "", false 64 | } 65 | 66 | return signned, true 67 | } -------------------------------------------------------------------------------- /polkadotTransaction/txEra.go: -------------------------------------------------------------------------------- 1 | package polkadotTransaction 2 | 3 | 4 | const calPeriod = 64 5 | 6 | func GetEra(height uint64) []byte { 7 | 8 | phase := height % calPeriod 9 | 10 | index := uint64(6) 11 | trailingZero := index - 1 12 | 13 | var encoded uint64 14 | if trailingZero > 1 { 15 | encoded = trailingZero 16 | } else { 17 | encoded = 1 18 | } 19 | 20 | if trailingZero < 15 { 21 | encoded = trailingZero 22 | } else { 23 | encoded = 15 24 | } 25 | 26 | encoded += phase / 1 << 4 27 | 28 | first := byte(encoded >> 8) 29 | second := byte(encoded & 0xff) 30 | 31 | return []byte{second, first} 32 | } 33 | -------------------------------------------------------------------------------- /polkadotTransaction/txEra_test.go: -------------------------------------------------------------------------------- 1 | package polkadotTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestGetEra(t *testing.T) { 10 | height := uint64(1767147) 11 | 12 | era := GetEra(height) 13 | 14 | fmt.Println(hex.EncodeToString(era)) 15 | } 16 | -------------------------------------------------------------------------------- /polkadotTransaction/txMethod.go: -------------------------------------------------------------------------------- 1 | package polkadotTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | ) 7 | 8 | type MethodTransfer struct { 9 | DestPubkey []byte 10 | Amount []byte 11 | } 12 | 13 | func NewMethodTransfer(pubkey string, amount uint64) (*MethodTransfer, error) { 14 | pubBytes, err := hex.DecodeString(pubkey) 15 | if err != nil || len(pubBytes) != 32 { 16 | return nil, errors.New("invalid dest public key") 17 | } 18 | 19 | if amount == 0 { 20 | return nil, errors.New("zero amount") 21 | } 22 | amountStr:= Encode( uint64(amount)) 23 | if err != nil { 24 | return nil, errors.New("invalid amount") 25 | } 26 | 27 | amountBytes, _ := hex.DecodeString(amountStr) 28 | 29 | return &MethodTransfer{ 30 | DestPubkey: pubBytes, 31 | Amount: amountBytes, 32 | }, nil 33 | } 34 | 35 | func (mt MethodTransfer) ToBytes(transferCode string) ([]byte, error) { 36 | 37 | if mt.DestPubkey == nil || len(mt.DestPubkey) != 32 || mt.Amount == nil || len(mt.Amount) == 0 { 38 | return nil, errors.New("invalid method") 39 | } 40 | 41 | ret, _ := hex.DecodeString(transferCode) 42 | if AccounntIDFollow { 43 | ret = append(ret, 0xff) 44 | } 45 | 46 | ret = append(ret, mt.DestPubkey...) 47 | ret = append(ret, mt.Amount...) 48 | 49 | return ret, nil 50 | } -------------------------------------------------------------------------------- /polkadotTransaction/txPayLoad.go: -------------------------------------------------------------------------------- 1 | package polkadotTransaction 2 | 3 | import "encoding/hex" 4 | 5 | type TxPayLoad struct { 6 | Method []byte 7 | Era []byte 8 | Nonce []byte 9 | Fee []byte 10 | SpecVersion []byte 11 | GenesisHash []byte 12 | BlockHash []byte 13 | TxVersion []byte 14 | } 15 | 16 | func (t TxPayLoad) ToBytesString () string { 17 | payload := make([]byte, 0) 18 | 19 | payload = append(payload, t.Method...) 20 | payload = append(payload, t.Era...) 21 | payload = append(payload, t.Nonce...) 22 | payload = append(payload, t.Fee...) 23 | payload = append(payload, t.SpecVersion...) 24 | payload = append(payload, t.TxVersion...) 25 | payload = append(payload, t.GenesisHash...) 26 | payload = append(payload, t.BlockHash...) 27 | 28 | return hex.EncodeToString(payload) 29 | } -------------------------------------------------------------------------------- /polkadotTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package polkadotTransaction 2 | 3 | const ( 4 | KSM_Balannce_Transfer = "040000" 5 | DOT_Balannce_Transfer = "050000" 6 | Balannce_Transfer_name = "transfer" 7 | Default_Period = 50 8 | SigningBitV4 = byte(0x84) 9 | Compact_U32 = "Compact" 10 | AccounntIDFollow = false 11 | ) 12 | 13 | const ( 14 | modeBits = 2 15 | singleMode byte = 0 16 | twoByteMode byte = 1 17 | fourByteMode byte = 2 18 | bigIntMode byte = 3 19 | singleModeMaxValue = 63 20 | twoByteModeMaxValue = 16383 21 | fourByteModeMaxValue = 1073741823 22 | ) 23 | var modeToNumOfBytes = map[byte]uint{ 24 | singleMode: 1, 25 | twoByteMode: 2, 26 | fourByteMode: 4, 27 | } -------------------------------------------------------------------------------- /rippleTransaction/enc_test.go: -------------------------------------------------------------------------------- 1 | package rippleTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_enc(t *testing.T) { 10 | fmt.Println("MemoType : ", hex.EncodeToString(getEncBytes(encodings["MemoType"]))) 11 | fmt.Println("MemoData : ", hex.EncodeToString(getEncBytes(encodings["MemoData"]))) 12 | fmt.Println("MemoFormat : ", hex.EncodeToString(getEncBytes(encodings["MemoFormat"]))) 13 | } 14 | -------------------------------------------------------------------------------- /rippleTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package rippleTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | "strings" 7 | 8 | owcrypt "github.com/blocktree/go-owcrypt" 9 | ) 10 | 11 | func CreateEmptyRawTransactionAndHash(from, pubkey string, destinationTag int64, sequence uint32, to string, amount, fee uint64, lastLedgerSequence uint32, memoType, memoData, memoFormat string) (string, string, error) { 12 | tx, err := NewTxStruct(from, pubkey,sequence , to, amount, fee, "",destinationTag, lastLedgerSequence, memoType, memoData, memoFormat) 13 | if err != nil { 14 | return "", "", err 15 | } 16 | return tx.ToEmptyRawWiths(), hex.EncodeToString(tx.GetHash()), nil 17 | } 18 | 19 | func SignRawTransaction(hash string, prikey []byte) (string, error) { 20 | hashBytes, err := hex.DecodeString(hash) 21 | if err != nil { 22 | return "", errors.New("Invalid transaction hash string!") 23 | } 24 | signature,_, reCode := owcrypt.Signature(prikey, nil, hashBytes, owcrypt.ECC_CURVE_SECP256K1) 25 | if reCode != owcrypt.SUCCESS { 26 | return "", errors.New("failed to sign transaction hash!") 27 | } 28 | return hex.EncodeToString(serilizeS(signature)), nil 29 | } 30 | 31 | func VerifyAndCombinRawTransaction(emptyTrans string, signature, publicKey string) (bool, string) { 32 | hash, err := getHashFromEmptyRawHex(emptyTrans) 33 | if err != nil { 34 | return false, "" 35 | } 36 | pubkeyBytes, err := hex.DecodeString(publicKey) 37 | if err != nil { 38 | return false, "" 39 | } 40 | sigBytes, err := hex.DecodeString(signature) 41 | if err != nil { 42 | return false, "" 43 | } 44 | pubkeyBytes = owcrypt.PointDecompress(pubkeyBytes, owcrypt.ECC_CURVE_SECP256K1)[1:] 45 | if owcrypt.SUCCESS != owcrypt.Verify(pubkeyBytes, nil, hash, sigBytes, owcrypt.ECC_CURVE_SECP256K1) { 46 | return false, "" 47 | } 48 | txnSignature, _ := getTxnSignatureBytes(signature) 49 | return true, strings.Replace(emptyTrans, "s", hex.EncodeToString(txnSignature), -1) 50 | } 51 | -------------------------------------------------------------------------------- /rippleTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package rippleTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func TestTransaction(t *testing.T) { 10 | from := "rHb9CJAWyB4rj91VRWn96DkukG4bwdtyTh" 11 | pubkey := "0330e7fc9d56bb25d6893ba3f317ae5bcf33b3291bd63db32654a313222f7fd020" 12 | to := "rb1fWuuAEtPUaeEWxocV3h4x5JwDTFZzH" 13 | sequence := uint32(1) 14 | amount := uint64(2000000000) 15 | fee := uint64(10000) 16 | lastLedgerSequence := uint32(353535) 17 | memoType := "client" 18 | memoData := "111" 19 | memoFormat := "text/plain" 20 | destinationTag := int64(1234) 21 | 22 | emptyTrans, hash, err := CreateEmptyRawTransactionAndHash(from, pubkey, destinationTag, sequence, to, amount, fee, lastLedgerSequence, memoType, memoData, memoFormat) 23 | if err != nil { 24 | t.Error(err) 25 | } else { 26 | fmt.Println("empty transaction : \n", emptyTrans) 27 | fmt.Println("transaction hash : \n", hash) 28 | } 29 | 30 | prikey, _ := hex.DecodeString("1acaaedece405b2a958212629e16f2eb46b153eee94cdd350fdeff52795525b7") 31 | signature, err := SignRawTransaction(hash, prikey) 32 | if err != nil { 33 | t.Error(err) 34 | } else { 35 | //signature = "168a76d7bef92f30761a03c0d039f9f018d32af756548f6e9ef41d1098d94ab5343513ca97f6437beca53b2e2ef36ee79a8fd2f4397473dd0031c12df11e6b83" 36 | fmt.Println("signature data : \n", signature) 37 | } 38 | 39 | // 40 | pass, signedTrans := VerifyAndCombinRawTransaction(emptyTrans, signature, pubkey) 41 | if pass { 42 | fmt.Println("signed transaction : \n", signedTrans) 43 | } else { 44 | t.Error("Verify transaction failed!") 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /rippleTransaction/txEnc.go: -------------------------------------------------------------------------------- 1 | package rippleTransaction 2 | 3 | type enc struct { 4 | typ, field byte 5 | } 6 | 7 | func getEncBytes(e enc) []byte { 8 | switch { 9 | case e.typ < 16 && e.field < 16: 10 | return []byte{e.typ<<4 | e.field} 11 | case e.typ < 16: 12 | return []byte{e.typ << 4, e.field} 13 | case e.field < 16: 14 | return []byte{e.field, e.typ} 15 | default: 16 | return []byte{0, e.typ, e.field} 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rippleTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package rippleTransaction 2 | 3 | const ( 4 | AddressPrefix byte = 0x00 5 | PAYMENT uint16 = 0x00 6 | HP_TRANSACTION_SIGN uint32 = 0x53545800 7 | TxCanonicalSignature uint32 = 0x80000000 8 | ) 9 | 10 | const ( 11 | ST_UINT16 uint8 = 1 12 | ST_UINT32 uint8 = 2 13 | ST_AMOUNT uint8 = 6 14 | ST_VL uint8 = 7 15 | ST_ACCOUNT uint8 = 8 16 | ST_OBJECT uint8 = 14 17 | ST_ARRAY uint8 = 15 18 | ) 19 | 20 | var ( 21 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 22 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 23 | ) 24 | 25 | var encodings = map[string]enc{ 26 | "TransactionType": enc{ST_UINT16, 2}, 27 | "Flags": enc{ST_UINT32, 2}, 28 | "Sequence": enc{ST_UINT32, 4}, 29 | "DestinationTag": enc{ST_UINT32, 14}, 30 | "LastLedgerSequence": enc{ST_UINT32, 27}, 31 | "Amount": enc{ST_AMOUNT, 1}, 32 | "Fee": enc{ST_AMOUNT, 8}, 33 | "SigningPubKey": enc{ST_VL, 3}, 34 | "TxnSignature": enc{ST_VL, 4}, 35 | "Account": enc{ST_ACCOUNT, 1}, 36 | "Owner": enc{ST_ACCOUNT, 2}, 37 | "Destination": enc{ST_ACCOUNT, 3}, 38 | "Memos": enc{ST_ARRAY, 9}, 39 | "Memo": enc{ST_OBJECT, 10}, 40 | "MemoType": enc{ST_VL, 12}, 41 | "MemoData": enc{ST_VL, 13}, 42 | "MemoFormat": enc{ST_VL, 14}, 43 | "EndOfObject": enc{ST_OBJECT, 1}, 44 | "EndOfArray": enc{ST_ARRAY, 1}, 45 | } 46 | -------------------------------------------------------------------------------- /rippleTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package rippleTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "math/big" 6 | ) 7 | 8 | type Value struct { 9 | native bool 10 | negative bool 11 | num uint64 12 | offset int64 13 | } 14 | 15 | func newValue(native, negative bool, num uint64, offset int64) *Value { 16 | return &Value{ 17 | native: native, 18 | negative: negative, 19 | num: num, 20 | offset: offset, 21 | } 22 | } 23 | 24 | func (v Value) IsNative() bool { 25 | return v.native 26 | } 27 | 28 | func (v *Value) Bytes() []byte { 29 | if v == nil { 30 | return nil 31 | } 32 | var u uint64 33 | if !v.negative && (v.num > 0 || v.IsNative()) { 34 | u |= 1 << 62 35 | } 36 | if v.IsNative() { 37 | u |= v.num & ((1 << 62) - 1) 38 | } else { 39 | u |= 1 << 63 40 | u |= v.num & ((1 << 54) - 1) 41 | if v.num > 0 { 42 | u |= uint64(v.offset+97) << 54 43 | } 44 | } 45 | var b [8]byte 46 | binary.BigEndian.PutUint64(b[:], u) 47 | return b[:] 48 | } 49 | 50 | func getSignatureBytes(sp []byte) []byte { 51 | r := sp[:32] 52 | s := sp[32:] 53 | if r[0]&0x80 == 0x80 { 54 | r = append([]byte{0x00}, r...) 55 | } else { 56 | for i := 0; i < 32; i++ { 57 | if r[0] == 0 && r[1]&0x80 != 0x80 { 58 | r = r[1:] 59 | } else { 60 | break 61 | } 62 | } 63 | } 64 | if s[0]&0x80 == 0x80 { 65 | s = append([]byte{0}, s...) 66 | } else { 67 | for i := 0; i < 32; i++ { 68 | if s[0] == 0 && s[1]&0x80 != 0x80 { 69 | s = s[1:] 70 | } else { 71 | break 72 | } 73 | } 74 | } 75 | 76 | r = append([]byte{byte(len(r))}, r...) 77 | r = append([]byte{0x02}, r...) 78 | s = append([]byte{byte(len(s))}, s...) 79 | s = append([]byte{0x02}, s...) 80 | 81 | rs := append(r, s...) 82 | rs = append([]byte{byte(len(rs))}, rs...) 83 | rs = append([]byte{0x30}, rs...) 84 | rs = append([]byte{byte(len(rs))}, rs...) 85 | 86 | return rs 87 | } 88 | 89 | func getPublicKeyBytes(pubkey []byte) []byte { 90 | return append([]byte{byte(len(pubkey))}, pubkey...) 91 | } 92 | 93 | func getHashBytes(hash []byte) []byte { 94 | return append([]byte{byte(len(hash))}, hash...) 95 | } 96 | 97 | func serilizeS(sig []byte) []byte { 98 | s := sig[32:] 99 | numS := new(big.Int).SetBytes(s) 100 | numHalfOrder := new(big.Int).SetBytes(HalfCurveOrder) 101 | if numS.Cmp(numHalfOrder) > 0 { 102 | numOrder := new(big.Int).SetBytes(CurveOrder) 103 | numS.Sub(numOrder, numS) 104 | 105 | s = numS.Bytes() 106 | if len(s) < 32 { 107 | for i := 0; i < 32-len(s); i++ { 108 | s = append([]byte{0x00}, s...) 109 | } 110 | } 111 | return append(sig[:32], s...) 112 | } 113 | return sig 114 | } 115 | 116 | func memoToBytes(memo string) []byte { 117 | return append([]byte{byte(len(memo))}, []byte(memo)...) 118 | } 119 | -------------------------------------------------------------------------------- /signatureSet/vsysSignature.go: -------------------------------------------------------------------------------- 1 | package signatureSet 2 | 3 | import ( 4 | owcrypt "github.com/blocktree/go-owcrypt" 5 | ) 6 | 7 | func VSYSSignature(prikey, msg []byte) ([]byte, uint16) { 8 | return owcrypt.Signature(prikey, nil, 0, msg, uint16(len(msg)), owcrypt.ECC_CURVE_X25519) 9 | } 10 | -------------------------------------------------------------------------------- /vergeTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package vergeTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | 7 | owcrypt "github.com/blocktree/go-owcrypt" 8 | ) 9 | 10 | type Vin struct { 11 | TxID string 12 | Vout uint32 13 | LockScript string 14 | } 15 | 16 | type Vout struct { 17 | Address string 18 | Amount uint64 19 | } 20 | 21 | func CreateEmptyTransactionAndHash(vin []Vin, vout []Vout, lockTime, timeInterval uint32) (string, []string, error) { 22 | txStruct, err := NewTxStruct(vin, vout, timeInterval, lockTime) 23 | if err != nil { 24 | return "", nil, err 25 | } 26 | 27 | hashes, _ := txStruct.GetHash() 28 | 29 | txHex := hex.EncodeToString(txStruct.ToBytes()) 30 | for _, in := range vin { 31 | txHex += ":" + in.LockScript 32 | } 33 | 34 | return txHex, hashes, nil 35 | } 36 | 37 | func SignTransaction(hash string, prikey []byte) ([]byte, error) { 38 | hashBytes, err := hex.DecodeString(hash) 39 | if err != nil { 40 | return nil, errors.New("Invalid hash!") 41 | } 42 | 43 | signature,_, retCode := owcrypt.Signature(prikey, nil, hashBytes, owcrypt.ECC_CURVE_SECP256K1) 44 | if retCode != owcrypt.SUCCESS { 45 | return nil, errors.New("Sign Failed!") 46 | } 47 | signature = serilizeS(signature) 48 | return signature, nil 49 | } 50 | 51 | func VerifyAndCombineTransaction(emptyTrans string, sigPubs []SigPub) (bool, string, error) { 52 | 53 | txStruct, _, err := DecodeTxStructRaw(emptyTrans) 54 | if err != nil { 55 | return false, "", err 56 | } 57 | if sigPubs == nil || len(sigPubs) == 0 || len(sigPubs) != len(txStruct.Vin) { 58 | return false, "", errors.New("inputs and signatures not match!") 59 | } 60 | 61 | hashes, err := txStruct.GetHash() 62 | if err != nil { 63 | return false, "", err 64 | } 65 | for index := 0; index < len(sigPubs); index++ { 66 | hashBytes, _ := hex.DecodeString(hashes[index]) 67 | pubkey := owcrypt.PointDecompress(sigPubs[index].Pubkey, owcrypt.ECC_CURVE_SECP256K1)[1:] 68 | if owcrypt.SUCCESS != owcrypt.Verify(pubkey, nil, hashBytes, sigPubs[index].Signature, owcrypt.ECC_CURVE_SECP256K1) { 69 | return false, "", errors.New("verify transaction failed!") 70 | } 71 | txStruct.Vin[index].SigPub = &sigPubs[index] 72 | } 73 | 74 | return true, hex.EncodeToString(txStruct.ToBytes()), nil 75 | } 76 | -------------------------------------------------------------------------------- /vergeTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package vergeTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_send(t *testing.T) { 10 | // 该案例参照链上交易 c23c4f9e8d5974a0de6b01af7e23875f8e48c93cfad5ff29723458e9df063014 进行构建 11 | // 如果希望得到与链上完全一致的结果,应在txStruct.go的第28行添加如下代码 12 | //timeuntil = 1558240781 13 | 14 | txid := "0c3fa57bd52ead464f40e761cb0a033bdf42fc06d80d02abbd17825bcede5b8f" 15 | vout := uint32(0) 16 | lockScript := "76a91457145147e7913e1bc41098efdd01a47ce2d1f3da88ac" 17 | 18 | to1 := "DGoZWrrKsiB7A3gCjpAuGXHYFS4im68zzV" 19 | amount1 := uint64(27532627216595) 20 | to2 := "DJx3PiAinkN8MRBhdoLzbHYTxU7xgSwq9d" 21 | amount2 := uint64(299938900000) 22 | 23 | // 构建输入 24 | vins := []Vin{Vin{txid, vout, lockScript}} 25 | // 构建输出 26 | vouts := []Vout{Vout{to1, amount1}, Vout{to2, amount2}} 27 | 28 | //其他参数 29 | lockTime := uint32(0) 30 | 31 | // XVG参数 32 | timeInterval := uint32(21600) 33 | 34 | // 构建交易单和待签哈希 35 | emptyTrans, hashes, err := CreateEmptyTransactionAndHash(vins, vouts, lockTime, timeInterval) 36 | if err != nil { 37 | t.Error("create failed!") 38 | } else { 39 | fmt.Println("空交易单:\n", emptyTrans) 40 | fmt.Println("待签哈希:\n", hashes[0]) 41 | } 42 | 43 | // 对交易单签名 44 | prikey := []byte{0x84, 0x98, 0x23, 0xd2, 0x2d, 0x81, 0xe4, 0x9e, 0xb7, 0x19, 0x06, 0x6b, 0xcf, 0x7e, 0xd1, 0x73, 0xe6, 0x09, 0x48, 0x22, 0xb0, 0xea, 0x4e, 0x79, 0x3f, 0x1d, 0x85, 0x97, 0xa5, 0x06, 0x0d, 0x27} 45 | signature, err := SignTransaction(hashes[0], prikey) 46 | if err != nil { 47 | t.Error("failed to sign!") 48 | } else { 49 | // only for test 50 | signature = []byte{0xed, 0x1b, 0xd0, 0x0b, 0xbb, 0xf5, 0xf5, 0x08, 0x63, 0x61, 0x09, 0x5b, 0x3d, 0x0a, 0xfd, 0x10, 0x31, 0x87, 0xa8, 0xe0, 0xdd, 0xea, 0x73, 0xcb, 0x0a, 0x8c, 0xf6, 0xa6, 0x9e, 0x67, 0xd8, 0xca, 0x62, 0xac, 0x1c, 0x2c, 0xe2, 0x63, 0x2f, 0xb7, 0xce, 0xea, 0x66, 0x32, 0x4b, 0xd6, 0x43, 0x93, 0x3e, 0xe2, 0xd8, 0xa2, 0xed, 0x5e, 0xab, 0xee, 0xdd, 0x14, 0x99, 0xc1, 0xd3, 0xc5, 0x30, 0x8e} 51 | fmt.Println("签名结果:\n", hex.EncodeToString(signature)) 52 | } 53 | 54 | // 验证合并 55 | pubkey := []byte{0x02, 0x40, 0x65, 0x3c, 0x17, 0xb3, 0x31, 0xf7, 0x64, 0x34, 0x22, 0x40, 0x8f, 0x3a, 0x2e, 0x37, 0x3b, 0xc4, 0x93, 0x15, 0x71, 0xc6, 0x27, 0xdd, 0x4c, 0xd9, 0xbf, 0x96, 0x43, 0x82, 0xfd, 0xe1, 0xc8} 56 | 57 | //构建签名 58 | sigPubs := []SigPub{SigPub{pubkey, signature}} 59 | 60 | pass, signedTrans, err := VerifyAndCombineTransaction(emptyTrans, sigPubs) 61 | if err != nil { 62 | t.Error("failed to verify!") 63 | } else { 64 | if pass != true { 65 | fmt.Println("verify failed!") 66 | } else { 67 | fmt.Println("待发送交易单:\n", signedTrans) 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /vergeTransaction/txIn.go: -------------------------------------------------------------------------------- 1 | package vergeTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | type TxIn struct { 10 | TxID []byte 11 | Vout []byte 12 | SigPub *SigPub 13 | Sequence []byte 14 | LockScript []byte 15 | } 16 | 17 | func (in Vin) NewTxIn() (*TxIn, error) { 18 | txidBytes, err := reverseHexToBytes(in.TxID) 19 | if err != nil { 20 | return nil, errors.New("Invalid txid!") 21 | } 22 | 23 | vout := make([]byte, 4) 24 | binary.LittleEndian.PutUint32(vout[:], in.Vout) 25 | lockScript, err := hex.DecodeString(in.LockScript) 26 | if err != nil { 27 | return nil, errors.New("invalid lock script!") 28 | } 29 | lockScript = append([]byte{byte(len(lockScript))}, lockScript...) 30 | return &TxIn{ 31 | TxID: txidBytes, 32 | Vout: vout, 33 | SigPub: nil, 34 | Sequence: []byte{0xff, 0xff, 0xff, 0xff}, 35 | LockScript: lockScript, 36 | }, nil 37 | } 38 | 39 | func (in TxIn) ToBytes() []byte { 40 | ret := []byte{} 41 | ret = append(ret, in.TxID...) 42 | ret = append(ret, in.Vout...) 43 | if in.SigPub == nil { 44 | ret = append(ret, byte(0)) 45 | } else { 46 | ret = append(ret, in.SigPub.ToBytes()...) 47 | } 48 | ret = append(ret, in.Sequence...) 49 | return ret 50 | } 51 | -------------------------------------------------------------------------------- /vergeTransaction/txOut.go: -------------------------------------------------------------------------------- 1 | package vergeTransaction 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | const ( 8 | p2pkhPrefix = byte(0x1E) 9 | p2shPrefix = byte(0x21) 10 | ) 11 | 12 | type TxOut struct { 13 | Amount []byte 14 | LockScript []byte 15 | } 16 | 17 | func (out Vout) NewTxOut() (*TxOut, error) { 18 | 19 | prefix, lockScript, err := DecodeCheck(out.Address) 20 | 21 | if err != nil { 22 | return nil, errors.New("Invalid address to send!") 23 | } 24 | 25 | if prefix == p2pkhPrefix { 26 | lockScript = append([]byte{0x19, 0x76, 0xa9, 0x14}, lockScript...) 27 | lockScript = append(lockScript, []byte{0x88, 0xAC}...) 28 | } else if prefix == p2shPrefix { 29 | lockScript = append([]byte{0x17, 0xa9, 0x14}, lockScript...) 30 | lockScript = append(lockScript, byte(0x87)) 31 | } else { 32 | return nil, errors.New("Unsupport address to send!") 33 | } 34 | 35 | return &TxOut{ 36 | Amount: uint64ToLittleEndianBytes(out.Amount), 37 | LockScript: lockScript, 38 | }, nil 39 | } 40 | 41 | func (out TxOut) ToBytes() []byte { 42 | ret := []byte{} 43 | ret = append(ret, out.Amount...) 44 | ret = append(ret, out.LockScript...) 45 | return ret 46 | } 47 | -------------------------------------------------------------------------------- /vergeTransaction/txUtil.go: -------------------------------------------------------------------------------- 1 | package vergeTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | //reverseBytes endian reverse 10 | func reverseBytes(s []byte) []byte { 11 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 12 | s[i], s[j] = s[j], s[i] 13 | } 14 | return s 15 | } 16 | 17 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 18 | func reverseHexToBytes(hexVar string) ([]byte, error) { 19 | if len(hexVar)%2 == 1 { 20 | return nil, errors.New("Invalid TxHash!") 21 | } 22 | ret, err := hex.DecodeString(hexVar) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return reverseBytes(ret), nil 27 | } 28 | 29 | //uint64ToLittleEndianBytes 30 | func uint64ToLittleEndianBytes(data uint64) []byte { 31 | tmp := [8]byte{} 32 | binary.LittleEndian.PutUint64(tmp[:], data) 33 | return tmp[:] 34 | } 35 | 36 | //littleEndianBytesToUint64 37 | func littleEndianBytesToUint64(data []byte) uint64 { 38 | return binary.LittleEndian.Uint64(data) 39 | } 40 | 41 | //uint64ToLittleEndianBytes 42 | func uint32ToLittleEndianBytes(data uint32) []byte { 43 | tmp := [4]byte{} 44 | binary.LittleEndian.PutUint32(tmp[:], data) 45 | return tmp[:] 46 | } 47 | -------------------------------------------------------------------------------- /virtualeconomyTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package virtualeconomyTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_case(t *testing.T) { 10 | amount := uint64(1000000000) 11 | fee := uint64(10000000) 12 | feeScale := uint16(100) 13 | to := "AU6GsBinGPqW8zUuvmjgwpBNLfyyTU3p83Q" 14 | attachment := "" 15 | 16 | // 构建结构体用于创建交易单 17 | ts := TxStruct{ 18 | TxType: TxTypeTransfer, 19 | To: to, 20 | Amount: amount, 21 | Fee: fee, 22 | FeeScale: feeScale, 23 | Attachment: attachment, 24 | } 25 | 26 | emptyTrans, err := CreateEmptyTransaction(ts) 27 | if err != nil { 28 | fmt.Println(err) 29 | t.Error("Failed to create empty raw transaction!") 30 | } else { 31 | fmt.Println(emptyTrans) 32 | } 33 | 34 | // emptyTrans 同时也是用于最终签名的值,该链不存在用于签名的哈希 35 | 36 | // 对空交易单进行签名 37 | prikey := []byte{0x18, 0x6f, 0xdc, 0x45, 0xdb, 0x17, 0x67, 0x2d, 0x00, 0x56, 0x22, 0x03, 0x8f, 0x4c, 0x9e, 0x1c, 0x42, 0x4a, 0xce, 0xe6, 0x61, 0x10, 0x8f, 0xc7, 0x0a, 0xde, 0xe9, 0xfb, 0x78, 0x71, 0xa5, 0x56} 38 | 39 | sigPub, err := SignTransaction(emptyTrans, prikey) 40 | if err != nil { 41 | fmt.Println(err) 42 | t.Error("Fail to sign transaction!") 43 | } else { 44 | fmt.Println("signature :") 45 | fmt.Println(hex.EncodeToString(sigPub.Signature)) 46 | fmt.Println("public key :") 47 | fmt.Println(hex.EncodeToString(sigPub.PublicKey)) 48 | } 49 | 50 | // 验证交易单签名 51 | // 该链不存在合并交易单操作 52 | pass := VerifyTransaction(emptyTrans, sigPub) 53 | if pass { 54 | fmt.Println("verify pass!") 55 | } else { 56 | t.Error("verify failed!") 57 | } 58 | 59 | // 构建用于发送交易单时的RPC接口的"POST"方法的json体字符串 60 | json, err := CreateJSONRawForSendTransaction(emptyTrans, sigPub) 61 | 62 | if err != nil { 63 | t.Error("create json failed!") 64 | } else { 65 | fmt.Println("JSON for send transaction!") 66 | fmt.Println(json) 67 | } 68 | 69 | // 用节点API的 /vsys/broadcast/payment POST以上json 即可发送该笔交易 70 | } 71 | -------------------------------------------------------------------------------- /virtualeconomyTransaction/txDecode.go: -------------------------------------------------------------------------------- 1 | package virtualeconomyTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "errors" 6 | ) 7 | 8 | func TxStructDecode(tx []byte) (*TxStruct, uint64, error) { 9 | var ts TxStruct 10 | 11 | limit := len(tx) 12 | index := 0 13 | 14 | if index+1 > limit { 15 | return nil, 0, errors.New("Invalid transaction data!") 16 | } 17 | ts.TxType = tx[index] 18 | index++ 19 | 20 | if index+8 > limit { 21 | return nil, 0, errors.New("Invalid transaction data!") 22 | } 23 | timestamp := binary.BigEndian.Uint64(tx[index : index+8]) 24 | index += 8 25 | 26 | if index+8 > limit { 27 | return nil, 0, errors.New("Invalid transaction data!") 28 | } 29 | ts.Amount = binary.BigEndian.Uint64(tx[index : index+8]) 30 | index += 8 31 | 32 | if index+8 > limit { 33 | return nil, 0, errors.New("Invalid transaction data!") 34 | } 35 | ts.Fee = binary.BigEndian.Uint64(tx[index : index+8]) 36 | index += 8 37 | 38 | if index+2 > limit { 39 | return nil, 0, errors.New("Invalid transaction data!") 40 | } 41 | ts.FeeScale = binary.BigEndian.Uint16(tx[index : index+2]) 42 | index += 2 43 | 44 | if index+26 > limit { 45 | return nil, 0, errors.New("Invalid transaction data!") 46 | } 47 | ts.To = Encode(tx[index:index+26], BitcoinAlphabet) 48 | index += 26 49 | 50 | if index+2 > limit { 51 | return nil, 0, errors.New("Invalid transaction data!") 52 | } 53 | if tx[index] == 0 && tx[index+1] == 0 { 54 | ts.Attachment = "" 55 | index += 2 56 | if index != limit { 57 | return nil, 0, errors.New("Invalid transaction data!") 58 | } 59 | 60 | } else { 61 | return nil, 0, errors.New("Non-empty attachment is not supported yet!") 62 | } 63 | 64 | return &ts, timestamp, nil 65 | } 66 | -------------------------------------------------------------------------------- /virtualeconomyTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package virtualeconomyTransaction 2 | 3 | var ( 4 | TxTypeTransfer = byte(2) 5 | DefaultFeeScale = uint16(100) 6 | DefaultFixedFee = uint64(10000000) 7 | ) 8 | -------------------------------------------------------------------------------- /vollarTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package vollarTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | 7 | owcrypt "github.com/blocktree/go-owcrypt" 8 | ) 9 | 10 | type Vin struct { 11 | TxID string 12 | Vout uint32 13 | LockScript string 14 | } 15 | 16 | type Vout struct { 17 | Address string 18 | Amount uint64 19 | } 20 | 21 | func CreateEmptyRawTransactionAndHash(vins []Vin, vouts []Vout) (string, []string, error) { 22 | 23 | txStruct, err := NewTxStruct(vins, vouts) 24 | if err != nil { 25 | return "", nil, err 26 | } 27 | 28 | hashes, err := txStruct.GetHash() 29 | if err != nil { 30 | return "", nil, err 31 | } 32 | lockScripts := "" 33 | for _, in := range vins { 34 | lockScripts += ":" + in.LockScript 35 | } 36 | return hex.EncodeToString(txStruct.ToBytes()) + lockScripts, hashes, nil 37 | } 38 | 39 | func SignRawTransaction(hash string, prikey []byte) ([]byte, error) { 40 | hashBytes, err := hex.DecodeString(hash) 41 | if err != nil { 42 | return nil, errors.New("invalid hash message") 43 | } 44 | sig,_, retCode := owcrypt.Signature(prikey, nil, hashBytes, owcrypt.ECC_CURVE_SECP256K1) 45 | 46 | if retCode != owcrypt.SUCCESS { 47 | return nil, errors.New("sign failed!") 48 | } 49 | 50 | return serilizeS(sig), nil 51 | } 52 | 53 | func VerifyAndCombineRawTransaction(emptyTrans string, sigPub []SigPub) (bool, string, error) { 54 | trans, lockScripts, err := DecodeTxStructRaw(emptyTrans) 55 | if err != nil { 56 | return false, "", err 57 | } 58 | 59 | if len(trans.Vin) != len(sigPub) || len(trans.Vin) != len(lockScripts) { 60 | return false, "", errors.New("signature and inputs are dismatched!") 61 | } 62 | for index := 0; index < len(lockScripts); index++ { 63 | ls, err := hex.DecodeString(lockScripts[index]) 64 | if err != nil { 65 | return false, "", errors.New("invalid lock script!") 66 | } 67 | trans.Vin[index].LockScript = append([]byte{byte(len(ls))}, ls...) 68 | } 69 | 70 | hashes, err := trans.GetHash() 71 | if err != nil { 72 | return false, "", err 73 | } 74 | 75 | pass := true 76 | 77 | for i := 0; i < len(sigPub); i++ { 78 | hash, _ := hex.DecodeString(hashes[i]) 79 | pubkey := owcrypt.PointDecompress(sigPub[i].Pubkey, owcrypt.ECC_CURVE_SECP256K1)[1:] 80 | if owcrypt.SUCCESS != owcrypt.Verify(pubkey, nil, hash, sigPub[i].Signature, owcrypt.ECC_CURVE_SECP256K1) { 81 | pass = false 82 | } 83 | trans.Vin[i].SigPub = &sigPub[i] 84 | } 85 | 86 | txBytes := trans.ToBytes() 87 | for index := 0; index < 82+len(trans.Vin); index++ { 88 | txBytes = append(txBytes, byte(0)) 89 | } 90 | return pass, hex.EncodeToString(txBytes), nil 91 | } 92 | -------------------------------------------------------------------------------- /vollarTransaction/transaction_test.go: -------------------------------------------------------------------------------- 1 | package vollarTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | func Test_transaction(t *testing.T) { 10 | // 案例复现了链上交易 11 | // id为: d217c2d2047b8c7004c6a99fbd3fb97a80bc8eaf3b2f219c02ac866632c1944b 12 | // 为了使结果与链上数据一致,测试过程中修改了签名结果和公钥 13 | 14 | // utxo 15 | txid := "3ba0c1c5e8ce46391287942ef4dadd3edfddadbf5a61d3fb2cfd8638a2cbdeb4" 16 | vout := uint32(1) 17 | lockscript := "76a91481772013db2bf0d22428053c68bb24868c381b9988ac" 18 | 19 | in := Vin{txid, vout, lockscript} 20 | 21 | // 交易输出 22 | out1 := Vout{"VcezzxyRG8y5vXhaUUibrqygm4QSPbc6Z7F", uint64(690000000)} 23 | out2 := Vout{"VcimwAzkqBDdFhzTunS2hQFrzBuXGmBHYYB", uint64(44990000)} 24 | 25 | // 同时获取空交易单和待签哈希 26 | emptyTrans, hashes, err := CreateEmptyRawTransactionAndHash([]Vin{in}, []Vout{out1, out2}) 27 | 28 | if err != nil { 29 | t.Error("failed to create transaction!") 30 | } else { 31 | fmt.Println("空交易单: \n", emptyTrans) 32 | for i, hash := range hashes { 33 | fmt.Println("第 ", i, "个hash为:\n", hash) 34 | } 35 | } 36 | 37 | prikey := []byte{0x84, 0x98, 0x23, 0xd2, 0x2d, 0x81, 0xe4, 0x9e, 0xb7, 0x19, 0x06, 0x6b, 0xcf, 0x7e, 0xd1, 0x73, 0xe6, 0x09, 0x48, 0x22, 0xb0, 0xea, 0x4e, 0x79, 0x3f, 0x1d, 0x85, 0x97, 0xa5, 0x06, 0x0d, 0x27} 38 | 39 | signature, err := SignRawTransaction(hashes[0], prikey) 40 | if err != nil { 41 | t.Error("failed to sign transaction!") 42 | } else { 43 | // only for test 44 | // 此处修改为与该笔交易对应的签名 45 | signature = []byte{0x3b, 0xeb, 0x79, 0x1e, 0x2d, 0x79, 0x4e, 0xcc, 0xad, 0xd1, 0x9b, 0x23, 0x62, 0xa5, 0xa1, 0x71, 0x75, 0xf1, 0x4e, 0x54, 0x7d, 0x8d, 0xd8, 0xee, 0xd2, 0xbc, 0xed, 0xa9, 0x0a, 0x3c, 0xe2, 0x00, 0x38, 0x6b, 0xe2, 0x6d, 0x86, 0xfc, 0x3a, 0x41, 0x2e, 0x08, 0xbc, 0xa9, 0x26, 0x71, 0x8f, 0x84, 0x28, 0xba, 0xba, 0x6f, 0xbd, 0x57, 0xeb, 0x00, 0xf7, 0xaa, 0x82, 0xcd, 0xc5, 0xfc, 0xe9, 0x60} 46 | fmt.Println("签名值为: \n", hex.EncodeToString(signature)) 47 | } 48 | 49 | pubkey := []byte{0x03, 0x57, 0xcb, 0x81, 0xef, 0xed, 0x4e, 0x5c, 0x86, 0x9d, 0xb1, 0x51, 0xaf, 0x95, 0x6b, 0xbf, 0x89, 0x56, 0xa8, 0x17, 0x77, 0x15, 0xb1, 0x3a, 0xdb, 0x6c, 0x93, 0x16, 0x94, 0xaa, 0x8e, 0x7a, 0x37} 50 | 51 | sigPub := SigPub{ 52 | Pubkey: pubkey, 53 | Signature: signature, 54 | } 55 | 56 | // 同时获取验签结果和最终合并交易单 57 | pass, signedTrans, err := VerifyAndCombineRawTransaction(emptyTrans, []SigPub{sigPub}) 58 | 59 | if err != nil { 60 | t.Error("verify failed!") 61 | } else { 62 | fmt.Println(pass) 63 | fmt.Println(signedTrans) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /vollarTransaction/txIn.go: -------------------------------------------------------------------------------- 1 | package vollarTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | type TxIn struct { 10 | TxID []byte 11 | Vout []byte 12 | SigPub *SigPub 13 | Sequence []byte 14 | LockScript []byte 15 | } 16 | 17 | func (in Vin) NewTxIn() (*TxIn, error) { 18 | txidBytes, err := reverseHexToBytes(in.TxID) 19 | if err != nil { 20 | return nil, errors.New("Invalid txid!") 21 | } 22 | 23 | vout := make([]byte, 4) 24 | binary.LittleEndian.PutUint32(vout[:], in.Vout) 25 | lockScript, err := hex.DecodeString(in.LockScript) 26 | if err != nil { 27 | return nil, errors.New("invalid lock script!") 28 | } 29 | lockScript = append([]byte{byte(len(lockScript))}, lockScript...) 30 | return &TxIn{ 31 | TxID: txidBytes, 32 | Vout: vout, 33 | SigPub: nil, 34 | Sequence: []byte{0xff, 0xff, 0xff, 0xff}, 35 | LockScript: lockScript, 36 | }, nil 37 | } 38 | 39 | func (in TxIn) ToBytes() []byte { 40 | ret := []byte{} 41 | ret = append(ret, in.TxID...) 42 | ret = append(ret, in.Vout...) 43 | if in.SigPub == nil { 44 | ret = append(ret, byte(0)) 45 | } else { 46 | ret = append(ret, in.SigPub.ToBytes()...) 47 | } 48 | ret = append(ret, in.Sequence...) 49 | return ret 50 | } 51 | -------------------------------------------------------------------------------- /vollarTransaction/txOut.go: -------------------------------------------------------------------------------- 1 | package vollarTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | ) 7 | 8 | const ( 9 | p2pkhPrefix = "101c" 10 | p2shPrefix = "1041" 11 | DefaultOutFlag = byte(0) 12 | ) 13 | 14 | var FixedHashData = []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00} 15 | 16 | type TxOut struct { 17 | Amount []byte 18 | Flag byte 19 | LockScript []byte 20 | HashData []byte 21 | } 22 | 23 | func (out Vout) NewTxOut() (*TxOut, error) { 24 | prefix, lockScript, err := DecodeCheck(out.Address) 25 | 26 | if err != nil { 27 | return nil, errors.New("Invalid address to send!") 28 | } 29 | 30 | if hex.EncodeToString(prefix) == p2pkhPrefix { 31 | lockScript = append([]byte{0x19, 0x76, 0xa9, 0x14}, lockScript...) 32 | lockScript = append(lockScript, []byte{0x88, 0xAC}...) 33 | } else if hex.EncodeToString(prefix) == p2shPrefix { 34 | lockScript = append([]byte{0x17, 0xa9, 0x14}, lockScript...) 35 | lockScript = append(lockScript, byte(0x87)) 36 | } else { 37 | return nil, errors.New("Unsupport address to send!") 38 | } 39 | 40 | return &TxOut{ 41 | Amount: uint64ToLittleEndianBytes(out.Amount), 42 | Flag: DefaultOutFlag, 43 | LockScript: lockScript, 44 | HashData: FixedHashData, 45 | }, nil 46 | } 47 | 48 | func (out TxOut) ToBytes() []byte { 49 | ret := []byte{} 50 | ret = append(ret, out.Amount...) 51 | ret = append(ret, out.Flag) 52 | ret = append(ret, out.LockScript...) 53 | ret = append(ret, out.HashData...) 54 | return ret 55 | } 56 | -------------------------------------------------------------------------------- /vollarTransaction/txUtil.go: -------------------------------------------------------------------------------- 1 | package vollarTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | //reverseBytes endian reverse 10 | func reverseBytes(s []byte) []byte { 11 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 12 | s[i], s[j] = s[j], s[i] 13 | } 14 | return s 15 | } 16 | 17 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 18 | func reverseHexToBytes(hexVar string) ([]byte, error) { 19 | if len(hexVar)%2 == 1 { 20 | return nil, errors.New("Invalid TxHash!") 21 | } 22 | ret, err := hex.DecodeString(hexVar) 23 | if err != nil { 24 | return nil, err 25 | } 26 | return reverseBytes(ret), nil 27 | } 28 | 29 | //uint64ToLittleEndianBytes 30 | func uint64ToLittleEndianBytes(data uint64) []byte { 31 | tmp := [8]byte{} 32 | binary.LittleEndian.PutUint64(tmp[:], data) 33 | return tmp[:] 34 | } 35 | 36 | //littleEndianBytesToUint64 37 | func littleEndianBytesToUint64(data []byte) uint64 { 38 | return binary.LittleEndian.Uint64(data) 39 | } 40 | -------------------------------------------------------------------------------- /waykichainTransaction/transaction.go: -------------------------------------------------------------------------------- 1 | package waykichainTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "errors" 6 | 7 | owcrypt "github.com/blocktree/go-owcrypt" 8 | ) 9 | 10 | // fromUserID - user id when txType is TxType_COMMON 11 | // fromUserID - publick key hex when txType is TxType_REGACCT 12 | // to - contract Hex when txType is TxType_CONTRACT 13 | // appID - contract regID when txType is TxType_CONTRACT 14 | // appID - coin name when txType is TxType_UcoinTransfer 15 | func CreateEmptyRawTransactionAndHash(fromUserID, to, appID string, amount, fee, validHeight int64, txType byte) (string, string, error) { 16 | if txType == TxType_COMMON { 17 | txCommon, err := NewCommonTx(fromUserID, to, amount, fee, validHeight) 18 | if err != nil { 19 | return "", "", err 20 | } 21 | return hex.EncodeToString(txCommon.ToBytes()), hex.EncodeToString(txCommon.GetHash()), nil 22 | } else if txType == TxType_REGACCT { 23 | txRegisterAccount, err := NewRegisterAccountTx(fromUserID, fee, validHeight) 24 | if err != nil { 25 | return "", "", err 26 | } 27 | 28 | return hex.EncodeToString(txRegisterAccount.ToBytes()), hex.EncodeToString(txRegisterAccount.GetHash()), nil 29 | } else if txType == TxType_CONTRACT { 30 | txContract, err := NewCallContractTx(fromUserID, appID, to, validHeight, fee, amount) 31 | if err != nil { 32 | return "", "", err 33 | } 34 | 35 | return hex.EncodeToString(txContract.ToBytes()), hex.EncodeToString(txContract.GetHash()), nil 36 | } else if txType == TxType_UcoinTransfer { 37 | txUcoinTransfer, err := NewUcoinTransferTx(fromUserID, to, appID,validHeight, fee, amount) 38 | if err != nil { 39 | return "", "", err 40 | } 41 | 42 | return hex.EncodeToString(txUcoinTransfer.ToBytes()), hex.EncodeToString(txUcoinTransfer.GetHash()), nil 43 | } 44 | return "", "", errors.New("Unknown transaction type") 45 | } 46 | 47 | func SignRawTransaction(hash string, prikey []byte) ([]byte, error) { 48 | hashBytes, err := hex.DecodeString(hash) 49 | if err != nil { 50 | return nil, errors.New("Invalid transaction hash string!") 51 | } 52 | 53 | signature,_, retCode := owcrypt.Signature(prikey, nil, hashBytes, owcrypt.ECC_CURVE_SECP256K1) 54 | if retCode != owcrypt.SUCCESS { 55 | return nil, errors.New("Failed to sign transaction hash!") 56 | } 57 | 58 | return signature, nil 59 | } 60 | 61 | func VerifyAndCombineRawTransaction(emptyTrans string, sigPub SigPub) (bool, string) { 62 | hash, err := getHashFromEmptyRawTrans(emptyTrans) 63 | if err != nil { 64 | return false, "" 65 | } 66 | pubkey := owcrypt.PointDecompress(sigPub.PublicKey, owcrypt.ECC_CURVE_SECP256K1)[1:] 67 | 68 | if owcrypt.SUCCESS != owcrypt.Verify(pubkey, nil, hash, sigPub.Signature, owcrypt.ECC_CURVE_SECP256K1) { 69 | return false, "" 70 | } 71 | 72 | return true, emptyTrans + hex.EncodeToString(sigPub.ToBytes()) 73 | } 74 | -------------------------------------------------------------------------------- /waykichainTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package waykichainTransaction 2 | 3 | const ( 4 | AddressPrefix = byte(0x49) 5 | AddressPrefixTestNet = byte(0x87) 6 | Version = int64(0x01) 7 | TxType_REWARD = byte(0x01) 8 | TxType_REGACCT = byte(0x02) 9 | TxType_COMMON = byte(0x03) 10 | TxType_CONTRACT = byte(0x04) 11 | TxType_UcoinTransfer = byte(0x0B) 12 | 13 | DefaultFeeSymbol = "WICC" 14 | ) 15 | -------------------------------------------------------------------------------- /waykichainTransaction/txSignature.go: -------------------------------------------------------------------------------- 1 | package waykichainTransaction 2 | 3 | type SigPub struct { 4 | PublicKey []byte 5 | Signature []byte 6 | } 7 | 8 | func (sp SigPub) ToBytes() []byte { 9 | r := sp.Signature[:32] 10 | s := sp.Signature[32:] 11 | if r[0]&0x80 == 0x80 { 12 | r = append([]byte{0x00}, r...) 13 | } else { 14 | for i := 0; i < 32; i++ { 15 | if r[0] == 0 && r[1]&0x80 != 0x80 { 16 | r = r[1:] 17 | } else { 18 | break 19 | } 20 | } 21 | } 22 | if s[0]&0x80 == 0x80 { 23 | s = append([]byte{0}, s...) 24 | } else { 25 | for i := 0; i < 32; i++ { 26 | if s[0] == 0 && s[1]&0x80 != 0x80 { 27 | s = s[1:] 28 | } else { 29 | break 30 | } 31 | } 32 | } 33 | 34 | r = append([]byte{byte(len(r))}, r...) 35 | r = append([]byte{0x02}, r...) 36 | s = append([]byte{byte(len(s))}, s...) 37 | s = append([]byte{0x02}, s...) 38 | 39 | rs := append(r, s...) 40 | rs = append([]byte{byte(len(rs))}, rs...) 41 | rs = append([]byte{0x30}, rs...) 42 | rs = append([]byte{byte(len(rs))}, rs...) 43 | 44 | return rs 45 | } 46 | -------------------------------------------------------------------------------- /waykichainTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package waykichainTransaction 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | func sizeofVarInt(value int64) int { 8 | ret := 0 9 | n := value 10 | 11 | for { 12 | ret++ 13 | if n <= 0x7f { 14 | break 15 | } 16 | n = (n >> 7) - 1 17 | } 18 | 19 | return ret 20 | } 21 | 22 | func reverseBytes(data []byte) []byte { 23 | ret := make([]byte, len(data)) 24 | for index := 0; index < len(data); index++ { 25 | ret[index] = data[len(data)-1-index] 26 | } 27 | return ret 28 | } 29 | 30 | func int64ToUvarint(value int64) []byte { 31 | size := sizeofVarInt(value) 32 | tmp := make([]byte, ((size*8 + 6) / 7)) 33 | len := 0 34 | n := value 35 | for { 36 | h := byte(0) 37 | if len == 0 { 38 | h = 0x00 39 | } else { 40 | h = 0x80 41 | } 42 | 43 | tmp[len] = byte((byte(n) & 0x7f) | h) 44 | 45 | if n <= 0x7f { 46 | break 47 | } 48 | 49 | n = (n >> 7) - 1 50 | len++ 51 | } 52 | return reverseBytes(tmp[:size]) 53 | } 54 | 55 | func isRegIdStr(regId string) bool { 56 | re := regexp.MustCompile(`^\s*(\d+)\-(\d+)\s*$`) 57 | return re.MatchString(regId) 58 | } 59 | -------------------------------------------------------------------------------- /zencashTransaction/base58_test.go: -------------------------------------------------------------------------------- 1 | package zencashTransaction 2 | 3 | import ( 4 | "encoding/hex" 5 | "fmt" 6 | "github.com/blocktree/go-owcrypt" 7 | "testing" 8 | ) 9 | 10 | func TestDecode(t *testing.T) { 11 | address := "znowCwfo4iz7mfndyqH4F2JLbWy19fESBsd" 12 | hash, _ :=Decode(address, BitcoinAlphabet) 13 | fmt.Println(hex.EncodeToString(hash)) 14 | 15 | chk := owcrypt.Hash(hash[:22], 0, owcrypt.HASH_ALG_DOUBLE_SHA256) 16 | 17 | fmt.Println(hex.EncodeToString(chk)) 18 | 19 | 20 | } 21 | 22 | func Test_tmp(t *testing.T) { 23 | data, _ := hex.DecodeString("5230ff2fd4a08b46c9708138ba45d4ed480aed088402d81dce274ecf01000000") 24 | fmt.Println(hex.EncodeToString(reverseBytes(data))) 25 | 26 | 27 | hash, _ := hex.DecodeString("faa54cf3b138bd587a7a12b1b2ca76d600a37ae3") 28 | fmt.Println(EncodeCheck(ZENMainnetAddressPrefix.P2PKHPrefix, hash)) 29 | } -------------------------------------------------------------------------------- /zencashTransaction/txProfile.go: -------------------------------------------------------------------------------- 1 | package zencashTransaction 2 | 3 | type AddressPrefix struct { 4 | P2PKHPrefix []byte 5 | P2WPKHPrefix []byte 6 | Bech32Prefix string 7 | } 8 | 9 | var ( 10 | ZENMainnetAddressPrefix = AddressPrefix{[]byte{0x20, 0x89}, []byte{0x20, 0x96}, "bc"} 11 | ) 12 | 13 | const ( 14 | DefaultTxVersion = uint32(1) 15 | DefaultHashType = uint32(1) 16 | MaxScriptElementSize = 520 17 | ) 18 | 19 | const ( 20 | SequenceFinal = uint32(0xFFFFFFFF) 21 | SequenceMaxBip125RBF = uint32(0xFFFFFFFD) 22 | ) 23 | 24 | const ( 25 | SegWitSymbol = byte(0) 26 | SegWitVersion = byte(1) 27 | SigHashAll = byte(1) 28 | ) 29 | 30 | const ( 31 | OpCodeHash160 = byte(0xA9) 32 | OpCodeEqual = byte(0x87) 33 | OpCodeEqualVerify = byte(0x88) 34 | OpCodeCheckSig = byte(0xAC) 35 | OpCodeDup = byte(0x76) 36 | OpCode_1 = byte(0x51) 37 | OpCheckMultiSig = byte(0xAE) 38 | OpPushData1 = byte(0x4C) 39 | OpPushData2 = byte(0x4D) 40 | OpPushData3 = byte(0x4E) 41 | OpCodeCheckBlockAtHeight = byte(0xB4) 42 | ) 43 | 44 | var ( 45 | CurveOrder = []byte{0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFE, 0xBA, 0xAE, 0xDC, 0xE6, 0xAF, 0x48, 0xA0, 0x3B, 0xBF, 0xD2, 0x5E, 0x8C, 0xD0, 0x36, 0x41, 0x41} 46 | HalfCurveOrder = []byte{0x7F, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x5D, 0x57, 0x6E, 0x73, 0x57, 0xA4, 0x50, 0x1D, 0xDF, 0xE9, 0x2F, 0x46, 0x68, 0x1B, 0x20, 0xA0} 47 | ) 48 | -------------------------------------------------------------------------------- /zencashTransaction/txUtils.go: -------------------------------------------------------------------------------- 1 | package zencashTransaction 2 | 3 | import ( 4 | "encoding/binary" 5 | "encoding/hex" 6 | "errors" 7 | ) 8 | 9 | func byteArrayCompare(a, b []byte) bool { 10 | if len(a) != len(b) { 11 | return false 12 | } 13 | for index := 0; index < len(a); index++ { 14 | if a[index] != b[index] { 15 | return false 16 | } 17 | } 18 | return true 19 | } 20 | 21 | //reverseBytes endian reverse 22 | func reverseBytes(s []byte) []byte { 23 | for i, j := 0, len(s)-1; i < j; i, j = i+1, j-1 { 24 | s[i], s[j] = s[j], s[i] 25 | } 26 | return s 27 | } 28 | 29 | //reverseHexToBytes decode a hex string to an byte array,then change the endian 30 | func reverseHexToBytes(hexVar string) ([]byte, error) { 31 | if len(hexVar)%2 == 1 { 32 | return nil, errors.New("Invalid TxHash!") 33 | } 34 | ret, err := hex.DecodeString(hexVar) 35 | if err != nil { 36 | return nil, err 37 | } 38 | return reverseBytes(ret), nil 39 | } 40 | 41 | //reverseBytesToHex change the endian of the input byte array then encode it to hex string 42 | func reverseBytesToHex(bytesVar []byte) string { 43 | return hex.EncodeToString(reverseBytes(bytesVar)) 44 | } 45 | 46 | //uint16ToLittleEndianBytes 47 | func uint16ToLittleEndianBytes(data uint16) []byte { 48 | tmp := [2]byte{} 49 | binary.LittleEndian.PutUint16(tmp[:], data) 50 | return tmp[:] 51 | } 52 | 53 | //littleEndianBytesToUint16 54 | func littleEndianBytesToUint16(data []byte) uint16 { 55 | return binary.LittleEndian.Uint16(data) 56 | } 57 | 58 | //uint32ToLittleEndianBytes 59 | func uint32ToLittleEndianBytes(data uint32) []byte { 60 | tmp := [4]byte{} 61 | binary.LittleEndian.PutUint32(tmp[:], data) 62 | return tmp[:] 63 | } 64 | 65 | //littleEndianBytesToUint32 66 | func littleEndianBytesToUint32(data []byte) uint32 { 67 | return binary.LittleEndian.Uint32(data) 68 | } 69 | 70 | //uint64ToLittleEndianBytes 71 | func uint64ToLittleEndianBytes(data uint64) []byte { 72 | tmp := [8]byte{} 73 | binary.LittleEndian.PutUint64(tmp[:], data) 74 | return tmp[:] 75 | } 76 | 77 | //littleEndianBytesToUint64 78 | func littleEndianBytesToUint64(data []byte) uint64 { 79 | return binary.LittleEndian.Uint64(data) 80 | } 81 | --------------------------------------------------------------------------------