/
reconstructor.go
139 lines (115 loc) · 3.47 KB
/
reconstructor.go
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package mandosexpressionreconstructor
import (
"bytes"
"encoding/hex"
"fmt"
"math/big"
"strconv"
"strings"
ei "github.com/ElrondNetwork/arwen-wasm-vm/mandos-go/expression/interpreter"
)
type ExprReconstructorHint uint64
const (
// NoHint indicates that the type if not known
NoHint ExprReconstructorHint = iota
// NumberHint hints that value should be a number
NumberHint
// AddressHint hints that value should be an address
AddressHint
// StrHint hints that value should be a string expression, e.g. a username, "str:..."
StrHint
// CodeHint hints that value should be a smart contract code, normally loaded from a file
CodeHint
)
const maxBytesInterpretedAsNumber = 15
// ExprReconstructor is a component that attempts to convert raw bytes to a human-readable format.
type ExprReconstructor struct{}
func (er *ExprReconstructor) Reconstruct(value []byte, hint ExprReconstructorHint) string {
switch hint {
case NumberHint:
return fmt.Sprintf("%d", big.NewInt(0).SetBytes(value))
case StrHint:
return fmt.Sprintf("str:%s", string(value))
case AddressHint:
return addressPretty(value)
case CodeHint:
return codePretty(value)
default:
return unknownByteArrayPretty(value)
}
}
func (er *ExprReconstructor) ReconstructFromBigInt(value *big.Int) string {
return er.Reconstruct(value.Bytes(), NumberHint)
}
func (er *ExprReconstructor) ReconstructFromUint64(value uint64) string {
return er.Reconstruct(big.NewInt(0).SetUint64(value).Bytes(), NumberHint)
}
func unknownByteArrayPretty(bytes []byte) string {
if len(bytes) == 0 {
return ""
}
// fully interpret as string
if canInterpretAsString(bytes) {
return fmt.Sprintf("0x%s (str:%s)", hex.EncodeToString(bytes), string(bytes))
}
// interpret as number
if len(bytes) < maxBytesInterpretedAsNumber {
asInt := big.NewInt(0).SetBytes(bytes)
return fmt.Sprintf("0x%s (%d)", hex.EncodeToString(bytes), asInt)
}
// default interpret as string with escaped bytes
return fmt.Sprintf("0x%s (str:%s)", hex.EncodeToString(bytes), strconv.Quote(string(bytes)))
}
func addressPretty(value []byte) string {
if len(value) != 32 {
return unknownByteArrayPretty(value)
}
// smart contract addresses
leadingZeros := make([]byte, ei.SCAddressNumLeadingZeros)
if bytes.Equal(value[:ei.SCAddressNumLeadingZeros], leadingZeros) {
if value[31] == byte('_') {
addrStr := string(value[ei.SCAddressNumLeadingZeros:])
addrStr = strings.TrimRight(addrStr, "_")
return fmt.Sprintf("sc:%s", addrStr)
} else {
// last byte is the shard id and is explicit
addrStr := string(value[ei.SCAddressNumLeadingZeros:31])
addrStr = strings.TrimRight(addrStr, "_")
shard_id := value[31]
return fmt.Sprintf("sc:%s#%x", addrStr, shard_id)
}
}
// regular addresses
if value[31] == byte('_') {
addrStr := string(value)
addrStr = strings.TrimRight(addrStr, "_")
return fmt.Sprintf("address:%s", addrStr)
} else {
// last byte is the shard id and is explicit
addrStr := string(value[:31])
addrStr = strings.TrimRight(addrStr, "_")
shard_id := value[31]
return fmt.Sprintf("address:%s#%02x", addrStr, shard_id)
}
}
func canInterpretAsString(bytes []byte) bool {
if len(bytes) == 0 {
return false
}
for _, b := range bytes {
if b < 32 || b > 126 {
return false
}
}
return true
}
func codePretty(bytes []byte) string {
if len(bytes) == 0 {
return ""
}
encoded := hex.EncodeToString(bytes)
if len(encoded) > 20 {
return fmt.Sprintf("0x%s...", encoded[:20])
}
return fmt.Sprintf("0x%s", encoded)
}