aboutsummaryrefslogtreecommitdiffstats
path: root/common/natspec/natspec_e2e_test.go
blob: a7fc5cb77db52e040bdb70a11fba6e30e174a40c (plain) (blame)
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
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
package natspec

import (
    "fmt"
    "io/ioutil"
    "os"
    "strings"
    "testing"

    "github.com/ethereum/go-ethereum/accounts"
    "github.com/ethereum/go-ethereum/common"
    "github.com/ethereum/go-ethereum/common/docserver"
    "github.com/ethereum/go-ethereum/common/resolver"
    "github.com/ethereum/go-ethereum/core"
    "github.com/ethereum/go-ethereum/core/state"
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/ethereum/go-ethereum/eth"
    xe "github.com/ethereum/go-ethereum/xeth"
)

const (
    testBalance = "10000000000000000000"

    testFileName = "long_file_name_for_testing_registration_of_URLs_longer_than_32_bytes.content"

    testNotice = "Register key `utils.toHex(_key)` <- content `utils.toHex(_content)`"

    testExpNotice = "Register key 0xadd1a7d961cff0242089674ec2ef6fca671ab15e1fe80e38859fc815b98d88ab <- content 0xb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7"

    testExpNotice2 = `About to submit transaction (NatSpec notice error: abi key does not match any method): {"params":[{"to":"%s","data": "0x31e12c20"}]}`

    testExpNotice3 = `About to submit transaction (no NatSpec info found for contract: content hash not found for '0x1392c62d05b2d149e22a339c531157ae06b44d39a674cce500064b12b9aeb019'): {"params":[{"to":"%s","data": "0x300a3bbfb3a2dea218de5d8bbe6c4645aadbf67b5ab00ecb1a9ec95dbdad6a0eed3e41a7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066696c653a2f2f2f746573742e636f6e74656e74"}]}`
)

const (
    testUserDoc = `
{
  "methods": {
    "register(uint256,uint256)": {
      "notice":  "` + testNotice + `"
    }
  },
  "invariants": [
    { "notice": "" }
  ],
  "construction": [
    { "notice": "" }
  ]
}
`
    testAbiDefinition = `
[{
  "name": "register",
  "constant": false,
  "type": "function",
  "inputs": [{
    "name": "_key",
    "type": "uint256"
  }, {
    "name": "_content",
    "type": "uint256"
  }],
  "outputs": []
}]
`

    testContractInfo = `
{
    "userDoc": ` + testUserDoc + `,
    "abiDefinition": ` + testAbiDefinition + `
}
`
)

type testFrontend struct {
    t *testing.T
    // resolver    *resolver.Resolver
    ethereum    *eth.Ethereum
    xeth        *xe.XEth
    coinbase    common.Address
    stateDb     *state.StateDB
    txc         uint64
    lastConfirm string
    wantNatSpec bool
}

func (self *testFrontend) UnlockAccount(acc []byte) bool {
    self.ethereum.AccountManager().Unlock(common.BytesToAddress(acc), "password")
    return true
}

func (self *testFrontend) ConfirmTransaction(tx string) bool {
    if self.wantNatSpec {
        ds, err := docserver.New("/tmp/")
        if err != nil {
            self.t.Errorf("Error creating DocServer: %v", err)
        }
        self.lastConfirm = GetNotice(self.xeth, tx, ds)
    }
    return true
}

func testEth(t *testing.T) (ethereum *eth.Ethereum, err error) {

    os.RemoveAll("/tmp/eth-natspec/")

    err = os.MkdirAll("/tmp/eth-natspec/keys", os.ModePerm)
    if err != nil {
        panic(err)
    }

    // create a testAddress
    ks := crypto.NewKeyStorePassphrase("/tmp/eth-natspec/keys")
    am := accounts.NewManager(ks)
    testAccount, err := am.NewAccount("password")
    if err != nil {
        panic(err)
    }
    testAddress := strings.TrimPrefix(testAccount.Address.Hex(), "0x")

    // set up mock genesis with balance on the testAddress
    core.GenesisData = []byte(`{
    "` + testAddress + `": {"balance": "` + testBalance + `"}
    }`)

    // only use minimalistic stack with no networking
    ethereum, err = eth.New(&eth.Config{
        DataDir:        "/tmp/eth-natspec",
        AccountManager: am,
        MaxPeers:       0,
    })

    if err != nil {
        panic(err)
    }

    return
}

func testInit(t *testing.T) (self *testFrontend) {
    // initialise and start minimal ethereum stack
    ethereum, err := testEth(t)
    if err != nil {
        t.Errorf("error creating ethereum: %v", err)
        return
    }
    err = ethereum.Start()
    if err != nil {
        t.Errorf("error starting ethereum: %v", err)
        return
    }

    // mock frontend
    self = &testFrontend{t: t, ethereum: ethereum}
    self.xeth = xe.New(ethereum, self)

    addr, _ := ethereum.Etherbase()
    self.coinbase = addr
    self.stateDb = self.ethereum.ChainManager().State().Copy()

    // initialise the registry contracts
    // self.resolver.CreateContracts(addr)
    resolver.New(self.xeth).CreateContracts(addr)
    self.applyTxs()
    // t.Logf("HashReg contract registered at %v", resolver.HashRegContractAddress)
    // t.Logf("URLHint contract registered at %v", resolver.UrlHintContractAddress)

    return

}

// this is needed for transaction to be applied to the state in testing
// the heavy lifing is done in XEth.ApplyTestTxs
// this is fragile,
// and does process leaking since xeth loops cannot quit safely
// should be replaced by proper mining with testDAG for easy full integration tests
func (self *testFrontend) applyTxs() {
    self.txc, self.xeth = self.xeth.ApplyTestTxs(self.stateDb, self.coinbase, self.txc)
    return
}

// end to end test
func TestNatspecE2E(t *testing.T) {
    // t.Skip()

    tf := testInit(t)
    defer tf.ethereum.Stop()

    // create a contractInfo file (mock cloud-deployed contract metadocs)
    // incidentally this is the info for the registry contract itself
    ioutil.WriteFile("/tmp/"+testFileName, []byte(testContractInfo), os.ModePerm)
    dochash := common.BytesToHash(crypto.Sha3([]byte(testContractInfo)))

    // take the codehash for the contract we wanna test
    // codehex := tf.xeth.CodeAt(resolver.HashRegContractAddress)
    codeb := tf.xeth.CodeAtBytes(resolver.HashRegContractAddress)
    codehash := common.BytesToHash(crypto.Sha3(codeb))

    // use resolver to register codehash->dochash->url
    registry := resolver.New(tf.xeth)
    _, err := registry.Register(tf.coinbase, codehash, dochash, "file:///"+testFileName)
    if err != nil {
        t.Errorf("error registering: %v", err)
    }
    // apply txs to the state
    tf.applyTxs()

    // NatSpec info for register method of HashReg contract installed
    // now using the same transactions to check confirm messages

    tf.wantNatSpec = true // this is set so now the backend uses natspec confirmation
    _, err = registry.RegisterContentHash(tf.coinbase, codehash, dochash)
    if err != nil {
        t.Errorf("error calling contract registry: %v", err)
    }

    if tf.lastConfirm != testExpNotice {
        t.Errorf("Wrong confirm message. expected '%v', got '%v'", testExpNotice, tf.lastConfirm)
    }

    // test unknown method
    exp := fmt.Sprintf(testExpNotice2, resolver.HashRegContractAddress)
    _, err = registry.SetOwner(tf.coinbase)
    if err != nil {
        t.Errorf("error setting owner: %v", err)
    }

    if tf.lastConfirm != exp {
        t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm)
    }

    // test unknown contract
    exp = fmt.Sprintf(testExpNotice3, resolver.UrlHintContractAddress)

    _, err = registry.RegisterUrl(tf.coinbase, dochash, "file:///test.content")
    if err != nil {
        t.Errorf("error registering: %v", err)
    }

    if tf.lastConfirm != exp {
        t.Errorf("Wrong confirm message, expected '%v', got '%v'", exp, tf.lastConfirm)
    }

}