-
Notifications
You must be signed in to change notification settings - Fork 15
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
--story=1
- Loading branch information
Showing
13 changed files
with
1,636 additions
and
73 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package main | ||
|
||
import ( | ||
"github.com/btcsuite/btcd/btcec/v2" | ||
"github.com/btcsuite/btcd/btcec/v2/schnorr" | ||
"github.com/btcsuite/btcd/btcutil" | ||
"github.com/btcsuite/btcd/chaincfg" | ||
"github.com/btcsuite/btcd/txscript" | ||
) | ||
|
||
func GetTapScriptAddress(pk *btcec.PublicKey, revealedScript []byte, net *chaincfg.Params) (btcutil.Address, error) { | ||
pubkey33 := pk.SerializeCompressed() | ||
if pubkey33[0] == 0x02 { | ||
pubkey33[0] = byte(txscript.BaseLeafVersion) | ||
} else { | ||
pubkey33[0] = byte(txscript.BaseLeafVersion) + 1 | ||
} | ||
|
||
controlBlock, err := txscript.ParseControlBlock( | ||
pubkey33, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
rootHash := controlBlock.RootHash(revealedScript) | ||
|
||
// Next, we'll construct the final commitment (creating the external or | ||
// taproot output key) as a function of this commitment and the | ||
// included internal key: taprootKey = internalKey + (tPoint*G). | ||
taprootKey := txscript.ComputeTaprootOutputKey( | ||
controlBlock.InternalKey, rootHash, | ||
) | ||
|
||
// If we convert the taproot key to a witness program (we just need to | ||
// serialize the public key), then it should exactly match the witness | ||
// program passed in. | ||
tapKeyBytes := schnorr.SerializePubKey(taprootKey) | ||
|
||
addr, err := btcutil.NewAddressTaproot( | ||
tapKeyBytes, | ||
net, | ||
) | ||
return addr, nil | ||
} | ||
func GetTaprootPubkey(pubkey *btcec.PublicKey, revealedScript []byte) (*btcec.PublicKey, error) { | ||
controlBlock := txscript.ControlBlock{} | ||
controlBlock.InternalKey = pubkey | ||
rootHash := controlBlock.RootHash(revealedScript) | ||
|
||
// Next, we'll construct the final commitment (creating the external or | ||
// taproot output key) as a function of this commitment and the | ||
// included internal key: taprootKey = internalKey + (tPoint*G). | ||
taprootKey := txscript.ComputeTaprootOutputKey( | ||
controlBlock.InternalKey, rootHash, | ||
) | ||
return taprootKey, nil | ||
} | ||
|
||
// GetP2TRAddress returns a taproot address for a given public key. | ||
func GetP2TRAddress(pubKey *btcec.PublicKey, net *chaincfg.Params) (string, error) { | ||
addr, err := getP2TRAddress(pubKey, net) | ||
if err != nil { | ||
return "", err | ||
|
||
} | ||
return addr.EncodeAddress(), nil | ||
} | ||
func getP2TRAddress(pubKey *btcec.PublicKey, net *chaincfg.Params) (btcutil.Address, error) { | ||
tapKey := txscript.ComputeTaprootKeyNoScript(pubKey) | ||
addr, err := btcutil.NewAddressTaproot( | ||
schnorr.SerializePubKey(tapKey), net, | ||
) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return addr, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/hex" | ||
"errors" | ||
"unicode/utf8" | ||
|
||
"github.com/btcsuite/btcd/btcec/v2" | ||
"github.com/btcsuite/btcd/btcec/v2/schnorr" | ||
"github.com/btcsuite/btcd/btcutil" | ||
"github.com/btcsuite/btcd/chaincfg" | ||
"github.com/btcsuite/btcd/txscript" | ||
"github.com/bxelab/runestone" | ||
"lukechampine.com/uint128" | ||
) | ||
|
||
type Config struct { | ||
PrivateKey string | ||
FeePerByte int64 | ||
UtxoAmount int64 | ||
Network string | ||
RpcUrl string | ||
Etching *struct { | ||
Rune string | ||
Symbol *string | ||
Premine *uint64 | ||
Amount *uint64 | ||
Cap *uint64 | ||
Divisibility *int | ||
HeightStart *int | ||
HeightEnd *int | ||
HeightOffsetStart *int | ||
HeightOffsetEnd *int | ||
} | ||
Mint *struct { | ||
RuneId string | ||
} | ||
} | ||
|
||
func DefaultConfig() Config { | ||
return Config{ | ||
FeePerByte: 5, | ||
UtxoAmount: 1000, | ||
Network: "mainnet", | ||
RpcUrl: "https://mempool.space/api", | ||
} | ||
|
||
} | ||
func (c Config) GetFeePerByte() int64 { | ||
if c.FeePerByte == 0 { | ||
return 5 | ||
} | ||
return c.FeePerByte | ||
} | ||
func (c Config) GetUtxoAmount() int64 { | ||
if c.UtxoAmount == 0 { | ||
return 666 | ||
} | ||
return c.UtxoAmount | ||
} | ||
|
||
func (c Config) GetEtching() (*runestone.Etching, error) { | ||
if c.Etching == nil { | ||
return nil, errors.New("Etching config is required") | ||
} | ||
if c.Etching.Rune == "" { | ||
return nil, errors.New("Rune is required") | ||
} | ||
if c.Etching.Symbol != nil { | ||
runeCount := utf8.RuneCountInString(*c.Etching.Symbol) | ||
if runeCount != 1 { | ||
return nil, errors.New("Symbol must be a single character") | ||
} | ||
} | ||
etching := &runestone.Etching{} | ||
r, err := runestone.SpacedRuneFromString(c.Etching.Rune) | ||
if err != nil { | ||
return nil, err | ||
} | ||
etching.Rune = &r.Rune | ||
etching.Spacers = &r.Spacers | ||
if c.Etching.Symbol != nil { | ||
symbolStr := *c.Etching.Symbol | ||
symbol := rune(symbolStr[0]) | ||
etching.Symbol = &symbol | ||
} | ||
if c.Etching.Premine != nil { | ||
premine := uint128.From64(*c.Etching.Premine) | ||
etching.Premine = &premine | ||
} | ||
if c.Etching.Amount != nil { | ||
amount := uint128.From64(*c.Etching.Amount) | ||
if etching.Terms == nil { | ||
etching.Terms = &runestone.Terms{} | ||
} | ||
etching.Terms.Amount = &amount | ||
} | ||
if c.Etching.Cap != nil { | ||
cap := uint128.From64(*c.Etching.Cap) | ||
etching.Terms.Cap = &cap | ||
} | ||
if c.Etching.Divisibility != nil { | ||
d := uint8(*c.Etching.Divisibility) | ||
etching.Divisibility = &d | ||
} | ||
if c.Etching.HeightStart != nil { | ||
h := uint64(*c.Etching.HeightStart) | ||
if etching.Terms == nil { | ||
etching.Terms = &runestone.Terms{} | ||
} | ||
etching.Terms.Height[0] = &h | ||
} | ||
if c.Etching.HeightEnd != nil { | ||
h := uint64(*c.Etching.HeightEnd) | ||
if etching.Terms == nil { | ||
etching.Terms = &runestone.Terms{} | ||
} | ||
etching.Terms.Height[1] = &h | ||
} | ||
if c.Etching.HeightOffsetStart != nil { | ||
h := uint64(*c.Etching.HeightOffsetStart) | ||
if etching.Terms == nil { | ||
etching.Terms = &runestone.Terms{} | ||
} | ||
etching.Terms.Offset[0] = &h | ||
} | ||
if c.Etching.HeightOffsetEnd != nil { | ||
h := uint64(*c.Etching.HeightOffsetEnd) | ||
if etching.Terms == nil { | ||
etching.Terms = &runestone.Terms{} | ||
} | ||
etching.Terms.Offset[1] = &h | ||
} | ||
return etching, nil | ||
} | ||
func (c Config) GetMint() (*runestone.RuneId, error) { | ||
if c.Mint == nil { | ||
return nil, errors.New("Mint config is required") | ||
} | ||
if c.Mint.RuneId == "" { | ||
return nil, errors.New("RuneId is required") | ||
} | ||
runeId, err := runestone.RuneIdFromString(c.Mint.RuneId) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return runeId, nil | ||
} | ||
func (c Config) GetNetwork() *chaincfg.Params { | ||
if c.Network == "mainnet" { | ||
return &chaincfg.MainNetParams | ||
} | ||
if c.Network == "testnet" { | ||
return &chaincfg.TestNet3Params | ||
} | ||
if c.Network == "regtest" { | ||
return &chaincfg.RegressionNetParams | ||
} | ||
if c.Network == "signet" { | ||
return &chaincfg.SigNetParams | ||
} | ||
panic("unknown network") | ||
} | ||
|
||
func (c Config) GetPrivateKeyAddr() (*btcec.PrivateKey, string, error) { | ||
if c.PrivateKey == "" { | ||
return nil, "", errors.New("PrivateKey is required") | ||
} | ||
pkBytes, err := hex.DecodeString(c.PrivateKey) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
privKey, pubKey := btcec.PrivKeyFromBytes(pkBytes) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
tapKey := txscript.ComputeTaprootKeyNoScript(pubKey) | ||
addr, err := btcutil.NewAddressTaproot( | ||
schnorr.SerializePubKey(tapKey), c.GetNetwork(), | ||
) | ||
if err != nil { | ||
return nil, "", err | ||
} | ||
address := addr.EncodeAddress() | ||
return privKey, address, nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
PrivateKey: "1234567890" | ||
Network: "testnet" # mainnet or testnet | ||
RpcUrl: "https://blockstream.info/testnet/api" #https://mempool.space/api https://mempool.space/testnet/api | ||
FeePerByte: 5 | ||
UtxoAmount: 1000 | ||
Etching: | ||
Rune: "STUDYZY" | ||
Symbol: "曾" | ||
Premine: 1000000 | ||
Amount: 1000 | ||
Cap: 20000 | ||
# Divisibility: 0 | ||
# HeightStart: 0 | ||
# HeightEnd: 0 | ||
# HeightOffsetStart: 0 | ||
# HeightOffsetEnd: 0 | ||
Mint: | ||
RuneId: "2609649:946" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,75 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/hex" | ||
"encoding/json" | ||
"fmt" | ||
|
||
"github.com/btcsuite/btcd/txscript" | ||
"github.com/btcsuite/btcd/wire" | ||
"github.com/bxelab/runestone" | ||
"lukechampine.com/uint128" | ||
) | ||
|
||
func testEtching() { | ||
runeName := "STUDYZY.GMAIL.COM" | ||
symbol := '曾' | ||
myRune, err := runestone.SpacedRuneFromString(runeName) | ||
if err != nil { | ||
fmt.Println(err) | ||
return | ||
} | ||
amt := uint128.From64(666666) | ||
ca := uint128.From64(21000000) | ||
etching := &runestone.Etching{ | ||
Rune: &myRune.Rune, | ||
Spacers: &myRune.Spacers, | ||
Symbol: &symbol, | ||
Terms: &runestone.Terms{ | ||
Amount: &amt, | ||
Cap: &ca, | ||
}, | ||
} | ||
r := runestone.Runestone{Etching: etching} | ||
data, err := r.Encipher() | ||
if err != nil { | ||
fmt.Println(err) | ||
} | ||
fmt.Printf("Etching data: 0x%x\n", data) | ||
dataString, _ := txscript.DisasmString(data) | ||
fmt.Printf("Etching Script: %s\n", dataString) | ||
} | ||
func testMint() { | ||
runeIdStr := "2609649:946" | ||
runeId, _ := runestone.RuneIdFromString(runeIdStr) | ||
r := runestone.Runestone{Mint: runeId} | ||
data, err := r.Encipher() | ||
if err != nil { | ||
fmt.Println(err) | ||
} | ||
fmt.Printf("Mint Rune[%s] data: 0x%x\n", runeIdStr, data) | ||
dataString, _ := txscript.DisasmString(data) | ||
fmt.Printf("Mint Script: %s\n", dataString) | ||
} | ||
func testDecode() { | ||
data, _ := hex.DecodeString("140114001600") //Mint UNCOMMON•GOODS | ||
var tx wire.MsgTx | ||
builder := txscript.NewScriptBuilder() | ||
// Push opcode OP_RETURN | ||
builder.AddOp(txscript.OP_RETURN) | ||
// Push MAGIC_NUMBER | ||
builder.AddOp(runestone.MAGIC_NUMBER) | ||
// Push payload | ||
builder.AddData(data) | ||
pkScript, _ := builder.Script() | ||
txOut := wire.NewTxOut(0, pkScript) | ||
tx.AddTxOut(txOut) | ||
r := &runestone.Runestone{} | ||
artifact, err := r.Decipher(&tx) | ||
if err != nil { | ||
fmt.Println(err) | ||
return | ||
} | ||
a, _ := json.Marshal(artifact) | ||
fmt.Printf("Artifact: %s\n", string(a)) | ||
} |
Oops, something went wrong.