-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.go
More file actions
124 lines (110 loc) · 3.52 KB
/
script.go
File metadata and controls
124 lines (110 loc) · 3.52 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
// Package outscript generates potential output scripts for a given public key.
//
// It supports Bitcoin and Bitcoin-like cryptocurrency output script formats (P2PKH, P2SH, P2WPKH,
// P2WSH, P2PK, P2TR, etc.), EVM-based networks (Ethereum and compatible chains), and other
// blockchains such as Litecoin, Dogecoin, Namecoin, Monacoin, Electraproto, Dash, Bitcoin Cash,
// and Massa.
//
// The package also provides transaction building and signing for both Bitcoin-style (with
// segwit witness data) and EVM transactions, as well as block reward calculations for various
// cryptocurrency networks.
package outscript
import (
"crypto"
"crypto/ed25519"
"crypto/x509"
"fmt"
"slices"
)
// Script holds a public key and caches generated output scripts for various formats.
type Script struct {
pubkey crypto.PublicKey
cache map[string][]byte
}
// New returns a new [Script] object for the given public key, which can be used to generate output scripts
func New(pubkey crypto.PublicKey) *Script {
v := &Script{
pubkey: pubkey,
cache: make(map[string][]byte),
}
return v
}
// getPubKeyBytes returns the public key in the requested format (one of pubkey, pubkey:comp or pubkey:uncomp).
// pubkey:comp and pubkey:uncomp require a secp256k1 key
func (s *Script) getPubKeyBytes(typ string) ([]byte, error) {
switch typ {
case "pubkey:pkix":
return x509.MarshalPKIXPublicKey(s.pubkey)
case "pubkey:ed25519":
// get raw ed25519 key
if o, ok := s.pubkey.(ed25519.PublicKey); ok {
return []byte(o), nil
}
return nil, fmt.Errorf("pubkey of type %T does not support %s export", s.pubkey, typ)
case "pubkey:comp":
switch o := s.pubkey.(type) {
case interface{ SerializeCompressed() []byte }:
return o.SerializeCompressed(), nil
default:
return nil, fmt.Errorf("pubkey of type %T does not support %s export", s.pubkey, typ)
}
case "pubkey:uncomp":
switch o := s.pubkey.(type) {
case interface{ SerializeUncompressed() []byte }:
return o.SerializeUncompressed(), nil
default:
return nil, fmt.Errorf("pubkey of type %T does not support %s export", s.pubkey, typ)
}
default:
return nil, fmt.Errorf("unknown public key format %s", typ)
}
}
// Generate will return the byte value for the specified script type for the current public key
func (s *Script) Generate(name string) ([]byte, error) {
if r, ok := s.cache[name]; ok {
return r, nil
}
// some special cases to access the public key
switch name {
case "pubkey:pkix", "pubkey:ed25519", "pubkey:comp", "pubkey:uncomp":
res, err := s.getPubKeyBytes(name)
if err != nil {
return nil, err
}
s.cache[name] = res
return res, nil
}
f, ok := Formats[name]
if !ok {
return nil, fmt.Errorf("unsupported format %s", name)
}
var pieces [][]byte
for _, piece := range f {
v, err := piece.Bytes(s)
if err != nil {
return nil, err
}
pieces = append(pieces, v)
}
res := slices.Concat(pieces...)
s.cache[name] = res
return res, nil
}
// Out returns a [Out] object matching the requested script
func (s *Script) Out(name string) (*Out, error) {
buf, err := s.Generate(name)
if err != nil {
return nil, err
}
return makeOut(name, buf), nil
}
// Address formats the key using the specified script (eg. p2sh, etc) and optional
// flags hints. You can do things like Address("eth") where no hint is needed, or
// things like Address("p2pkh", "litecoin") so the appropriate format is used.
func (s *Script) Address(script string, flags ...string) (string, error) {
out, err := s.Out(script)
if err != nil {
return "", err
}
return out.Address(flags...)
}