File: blk03381.txt
/ViaBTC/Mined by meno5371/, 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474nd 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474nC FjDOUT:3D9F0A0449D91C3343F0BFEF3EB23AA18B601B147213F9A4559BA64718E8AF0E IjGREFUND:56077326FC10BAAB6E0230AF2A8E293F37BAF1EF14CD5A0D4F4C1C3FA04A0262 FjDOUT:96BECFE94CA30AD066515B74539F97AF51EABF2945E03DCC80D0A406DF0DA249 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n text/html;charset=utf-8 <!doctypehtml><html lang="en"><meta charset="UTF-8"><title>600 000 000 000</title><style>body{background:#f7931a;color:#fff;text-align:center}h1{font-weight:700;margin:0;font-size:666px;line-height:.9;font-family:sans}</style><h1>600 000 000 000</h1> GjE=:LTC.LTC:ltc1qtwfdwwp3adpwdgyvejj8k4mcrawnqvdr7gtzr6:2497261523:t:30 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n4% c/Foundry USA Pool #dropgold/ Bj@=:ETH.ETH:0xa34fc44e304de518b781bfb3eadef606e5fe9cab:204141549:t Aj?=:ETH.ETH:0x002eF02188CD5Af452662BAB7B0d217C98edc4c8:61461:te:0 Bj@=:BNB.BNB:bnb1z203xu68awz2ucee2ld92redjwh5kk4f7ttga0:260675:te:0 CjA=:BNB.BNB:bnb1277llrkm5luch2ac8p32g06qmwapczffgdy7d6:1135575:te:0 GjE=:BNB.BTCB-1DE:bnb1xnfqwtwzlxp5d6kk4teejkwt6n7kh665a5t86t:156826:te:0 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474nh DjB=:ETH.ETH:0xf3BF52129B6aB158bA6d52BB4eD9ba830D837744:23625389:te:0 c/Foundry USA Pool #dropgold/ 8j6BERNSTEIN 2.0 REG f039ea5c-096d-4530-8e95-756b5b652d79 FjD=:THOR.RUNE:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n:396898401658 7j5ion:10.QmY1BXnLuPWpG886pjZySeHZvfdF9QY5DYkmkw5PKcWRhc LjJs:GAIA.ATOM:cosmos1j0gdax9mfqk46msh25wh6l9gdhrqjdmeffw30e:13271266013:ss:0 Bj@=:ETH.ETH:0x4d7d7c6cC1171f52eEE75a4FAB1F45FAe66afdc2:182237:te:0 CjA=:BNB.BNB:bnb1xnfqwtwzlxp5d6kk4teejkwt6n7kh665a5t86t:2639585:te:0 JjH=:BNB.TWT-8C2:bnb1qfl4g38saelylhw6exkcf3ndcrtajy8k8248zk:1681715407:te:0 KjI=:BNB.BUSD-BD1:bnb1rwtf79az0wu49aju63kcaxayq7aql6qt6uq4mv:2310102804:te:0 FjDOUT:F862F6564CB1139A350C0E40300C50281EDBE5DB5292D1D4B715845C044BF004 IjGREFUND:86892234ED3FA22800E2B0461A4A63E11A976120D8618C42A0F444B5ECBD50F8 EjCs:ETH.ETH:0x749885b96c8989C32DF08480E5F064BcAE8C5Fa3:116120048:ss:0 CjA=:BNB.BNB:bnb17svwgfv49na3xz0e2v90aje5naht5y3k0xz23c:1564930:te:0 c/Foundry USA Pool #dropgold/ FjDOUT:55348E9171D3FE3300F4C09C34E021057116D71E8F309288045F0CF3E2D3DC5B FjDOUT:3DEA8A61D52864F6A2DB10F76B5BD3800276C0263DC542E5CEE0699A5131B53F FjDOUT:5B05C23B4CBB896A730AB2775BEBB53E74B3C8499FD1AD381083FE2D6B7B676A FjDOUT:42273D42823F4157B5640FD5177779052747563AEFC8BF880A152060143197E9 CjAs:DOGE.DOGE:DQB6j8npPxMAd5haECgcoHX2t33kwVSKBm:1036183033564:ss:0 SjLPs:ETH.USDT-13D831EC7:0x32EeD2A329794589294C321b76AE7fF49113a1C2:99371668544:ss:0 Bj@=:ETH.ETH:0xA34fc44E304dE518b781Bfb3eadEF606e5Fe9cAB:202490329:t2 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n 4j2DC-L5:UE664nEDRqD3F9LQMtKVrcqEvtCZwKLSYQyUr3iTMVE= =j;Stell Dir vor es ist Krieg und keiner geht hin fuck Putin Q /ViaBTC/Mined by amosss1/, KjISWAPTX:0x9da3abe6075f8e967bbb07467408cad6833ce759e7c03eeaf107d7902206e7e5 text/plain;charset=utf-8 Ordinals Puzzle #2 ... ... dropping soon on the timechain. This is a key piece to solving the puzzle. Follow @ordinalsindex on Twitter for updates. :j8=:RUNE:thor1y5mhjjz8pkzsyha06clwse64s2e48w0y4grttj::wr:0 /ViaBTC/Mined by fermakgbmk/, Bj@=:ETH.ETH:0x5e8f151E9bc6E30bc99c03b5e1D3198DE8d12D54:837634:te:0 Bj@=:BNB.BNB:bnb1hj0trl02xw5jyk9sgwyq2eraa5tzxf27tlvd72:340214:te:0 Bj@=:ETH.ETH:0x1D45997F8f6ea825A48Eb7aB5B9EbE035b86658c:124526:te:0 KjI=:BNB.TWT-8C2:bnb1jnfpv4jc9w7dam2k2436ny57460jcg36wfslsg:22309929123:te:0 LjJ=:BNB.BUSD-BD1:bnb1lp2c45aa6zdqdevtdjy5eg89t4zfw4tjlke8tj:32491936277:te:0 CjA=:BNB.BNB:bnb1hv9czre8fl6p39wah5f6vg3v9rxcd3pkassjez:2898773:te:0 1\ Powered by Luxor Tech \ 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n IjGREFUND:E74A26F47301A40D04B406B894AD3CC1E5FDD988C2B70B030CE70D66547BE3DB IjGREFUND:E19F8EF750D74D4456A618E44F81F5EAD1D6F6ED7EC405940C15FF9CA0826602 KjI=:BNB.BUSD-BD1:bnb1yhpptekq6n78d89w50d4d9ywgrle33p7dc0guv:5970795845:te:0 FjD=:LTC.LTC:ltc1qqx9v7r4e6s39u5twr22trf5lxnlt4qxsj9qfwj:169947468:t:30 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n SjLPs:ETH.USDC-E3606EB48:0x834bf675dAf52B5D6fD953a57c83e824521E5ab6:44729677632:ss:0 FjDOUT:0822E39D05D2A2EC8B3F7B76C24CB6A745AF54C82D29959E70BC6F1AA52D0FB9 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n4J FjDOUT:A9133BC8DB0377D8E62B2CBEC40E11E4FE3CF46FA7E4E727FFA3EFAFE844F5B4 FjDOUT:0B343EF4DB83F7E26A7E99923530DCDFA3A94D6C48EE918AC8D9A6DABAD86C0A FjDOUT:65C2EEA9B1495C224076CD341CE6F877E456AED4016092CDB09134C5C0A17FB5 FjDOUT:929E96C055817046DF38568F05ED344E1826363DF414D5A79DF7FBC0D38870F9 FjDOUT:BD9B469110DE9DE96318B455976D3F9028FBD2AE527B0B07B5377617B66F4570 FjDOUT:58808E85E050B8E58933F660199022A6A6EDF384AD197D378E6F9EDC5CD470DE DjB=:BNB.BNB:bnb1zf0k59rcpqr5r40p2acyqvsep07xklde6d5tv5:22297495:te:0 LjJ=:BNB.BUSD-BD1:bnb1lp2c45aa6zdqdevtdjy5eg89t4zfw4tjlke8tj:24531416280:te:0 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474nc FjDOUT:F66CAA38058DFE211215945AD96A60148C46D8868D78DAEC42D7FBF51B989FE6 FjDOUT:2004FE2E7DC28F5ABC76CFC86C0227F417E12E6E977277866815E3E461C65758 FjDOUT:D7E7451B4579E1E5F1E2367C124F9D7543AE0070D54EF5FB7F0CDA6FBCC6BF2B 7j5ion:15.QmbK6uUxWaBS8uqh6pd1iXp4ikQN3xa8AuoYrdvjNVDDzL Bj@=:ETH.ETH:0x422b204F960ea7A70144D4469ECE10EBE3F3f586:367636:te:0 c/Foundry USA Pool #dropgold/ KjISWAPTX:0x1a0276204b506559f45d1be3834b6e84cbbfee2f1b8e98d0712ec038e7d815b3 FjDOUT:6A3C40F228718A34B8F78AF954C1D62803A9ED5577D798279737EACF67598ABA FjDOUT:D39DDC050E29BD7172B573535E74CFDDA9CDA576843A557E603521A9C3725FF1 Bj@=:BNB.BNB:bnb1cxdmzc5dk8wrk8v5lr9zksmhzfmm0wwa4f90wr:441835:te:0 2ce981b37a0440f7353880d1546c91ecH0E c/Foundry USA Pool #dropgold/ 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n[ FjDOUT:E7F8B93686B68EA211F92E2FF95F6E1DEBC24DFC33DA41617BA5B6A34046E94D FjDOUT:E7255F20E202A6A113F374C435BA2D9DC38E590F9BF22B0A0FB1FCFFE7CA4A94 DjB=:BNB.BNB:bnb1xlrj688ksx7sw2qatvhrkful7t088l0r282wfd:28216438:te:0 KjI=:BNB.BUSD-BD1:bnb1lp2c45aa6zdqdevtdjy5eg89t4zfw4tjlke8tj:5987743462:te:0 text/html;charset=utf-8 <title>Basic Missile Command HTML Game</title> <meta charset="UTF-8"> background: black; align-items: center; justify-content: center; cursor: crosshair; border: 1px solid white; <canvas width="800" height="550" id="game"></canvas> const canvas = document.getElementById('game'); const context = canvas.getContext('2d');M const groundY = 500; // y position of where the ground starts const cityWidth = 45; // how wide a city rect is const cityHeight = 25; // how tall a city rect is const cityY = groundY - cityHeight; // y position of the city const siloY = groundY - 30; // y position of the top of the silo const missileSize = 4; // the radius/size of a missile const missileSpeed = 1; // how fast a missile moves const counterMissileSpeed = 15; // how fast a counter-missile moves // information about each missile let counterMissiles = []; // information about each explosion let explosions = []; // how many missiles to spawn at each interval of the level (in this // case spawn 4 missiles at the start of level 1 and 4 more missiles // at the next interval of level 1) const levels = [ [4, 4] ]; let currInterval = 0; // the x/y position of all cities and if the city is currently alive { x: 140, y: cityY, alive: true }, { x: 220, y: cityY, alive: true }, { x: 300, y: cityY, aliveM { x: 500, y: cityY, alive: true }, { x: 580, y: cityY, alive: true }, { x: 660, y: cityY, alive: true } // the x position of each of the 3 silos const siloPos = [ 55, canvas.width / 2, 745 ]; // the x/y position of each silo, the number of missiles left, and if // it is still alive { x: siloPos[0], y: siloY, missiles: 10, alive: true }, { x: siloPos[1], y: siloY, missiles: 10, alive: true }, { x: siloPos[2], y: siloY, missiles: 10, alive: true } // the x/y position of eaM ch missile spawn point. missiles spawn // directly above each city and silo plus the two edges const missileSpawns = cities .concat([{ x: 0, y: 0 }, { x: canvas.width, y: 0 }]) .map(pos => ({ x: pos.x, y: 0 })); // return a random integer between min (inclusive) and max (inclusive) // @see https://stackoverflow.com/a/1527820/2124254 function randInt(min, max) { return Math.floor(Math.random() * (max - min + 1)) + min; // get the angle between two points function angleBetweenPoints(source,M // atan2 returns the counter-clockwise angle in respect to the // x-axis, but the canvas rotation system is based on the y-axis // (rotation of 0 = up). // so we need to add a quarter rotation to return a // counter-clockwise rotation in respect to the y-axis return Math.atan2(target.y - source.y, target.x - source.x) + Math.PI / 2; // distance between two points function distance(source, target) { return Math.hypot(source.x - target.x, source.y - target.y); // spawn a missile by choM osing a spawn point and a target. // a missile can target any city or silo function spawnMissile() { const targets = cities.concat(silos); const randSpawn = randInt(0, missileSpawns.length - 1); const randTarget = randInt(0, targets.length - 1); const start = missileSpawns[randSpawn]; const target = targets[randTarget]; const angle = angleBetweenPoints(start, target); start, // where the missile started target, // where the missile is going pos: { x: start.x, y: start.y M }, // current position alive: true, // if we should still draw the missile // used to update the position every frame dx: missileSpeed * Math.sin(angle), dy: missileSpeed * -Math.cos(angle) // start at -2 seconds (time is in milliseconds) to give the player 1 // second before the missiles start let lastTime = -2000; function loop(time) { requestAnimationFrame(loop); context.clearRect(0,0,canvas.width,canvas.height); // spawn missiles every interval of 3 seconds (if thM if (time - lastTime > 3000 && currInterval < levels[currLevel].length) { for (let i = 0; i < levels[currLevel][currInterval]; i++) { spawnMissile(); lastTime = time; context.fillStyle = 'blue'; cities.forEach(city => { // center the city on the x position context.fillRect(city.x - cityWidth / 2, city.y, cityWidth, cityHeight); // draw ground and silos context.fillStyle = 'yellow'; context.moveTo(0, canvas.height); context.lineTo(0, groundY); // draw each silo hill siloPos.forEach(x => { context.lineTo(x - 40, groundY); context.lineTo(x - 20, siloY); context.lineTo(x + 20, siloY); context.lineTo(x + 40, groundY); context.lineTo(canvas.width, groundY); context.lineTo(canvas.width, canvas.height); // draw the number of counter-missiles each silo context.fillStyle = 'black'; silos.forEach(silo => { // draw missiles in aM triangular shape by incrementing how many // missiles we can draw per row let missilesPerRow = 1; let y = silo.y + 5; for (let i = 0; i < silo.missiles; i++) { context.fillRect(x, y, 4, 10); if (++count === missilesPerRow) { x = silo.x - 6 * count; missilesPerRow++; // update and draw missiles context.strokeStyle = 'red'; context.lineWidth = 2; te color based on time so it "blinks" // by dividing by a number and seeing if it's odd or even we can // change the speed of the blinking context.fillStyle = 'white'; if (Math.round(time / 2) % 2 === 0) { context.fillStyle = 'black'; missiles.forEach(missile => { missile.pos.x += missile.dx; missile.pos.y += missile.dy; // check if the missile hit an explosion by doing a circle-circle // collision check explosions.forEach(explosion => { const dist = distance(explosion,M if (dist < missileSize + explosion.size) { missile.alive = false; // if missile is close the the target we blow it up const dist = distance(missile.pos, missile.target); if (dist < missileSpeed) { missile.alive = false; missile.target.alive = false; if (missile.alive) { context.beginPath(); context.moveTo(missile.start.x, missile.start.y); context.lineTo(missile.pos.x, missile.pos.y); context.stroke(); center the head of the missile to the x/y position context.fillRect(missile.pos.x - missileSize / 2, missile.pos.y - missileSize / 2, missileSize, missileSize); // a dead missile spawns an explosion explosions.push({ x: missile.pos.x, y: missile.pos.y, // update and draw counter missiles context.strokeStyle = 'blue'; context.fillStyle = 'white'; counterMissiles.forEach(missile => { missile.pos.x += missile.dx; missile.pos.y += missile.dy; // if missile is close the the target we blow it up const dist = distance(missile.pos, missile.target); if (dist < counterMissileSpeed) { missile.alive = false; explosions.push({ x: missile.pos.x, y: missile.pos.y, context.beginPath(); context.moveTo(missile.start.x, missile.start.y); context.lineTo(missile.pos.x, M context.stroke(); context.fillRect(missile.pos.x - 2, missile.pos.y - 2, 4, 4); // update and draw explosions explosions.forEach(explosion => { explosion.size += 0.35 * explosion.dir; // change the direction of the explosion to wane if (explosion.size > 30) { explosion.dir = -1; // remove the explosion if (explosion.size <= 0) { explosion.alive = false; context.fillStyle = 'white'; if (Math.round(time M context.fillStyle = 'blue'; context.beginPath(); context.arc(explosion.x, explosion.y, explosion.size, 0, 2 * Math.PI); context.fill(); // remove dead missiles, explosions, cities, and silos missiles = missiles.filter(missile => missile.alive); counterMissiles = counterMissiles.filter(missile => missile.alive); explosions = explosions.filter(explosion => explosion.alive); cities = cities.filter(city => city.alive); silos = silos.filter(siM // listen to mouse events to fire counter-missiles canvas.addEventListener('click', e => { // get the x/y position of the mouse pointer by subtracting the x/y // position of the canvas element from the x/y position of the const x = e.clientX - e.target.offsetLeft; const y = e.clientY - e.target.offsetTop; // determine which silo is closest to the pointer and fire a // counter-missile from it let launchSilo = null; let siloDistance = Infinity; // start at the largM silos.forEach(silo => { const dist = distance({ x, y }, silo); if (dist < siloDistance && silo.missiles) { siloDistance = dist; launchSilo = silo; const start = { x: launchSilo.x, y: launchSilo.y }; const target = { x, y }; const angle = angleBetweenPoints(start, target); launchSilo.missiles--; counterMissiles.push({ pos: { x: launchSilo.x, y: launchSilo. y}, dx: counterMissileSpeed * Math.siL dy: counterMissileSpeed * -Math.cos(angle), requestAnimationFrame(loop); c/Foundry USA Pool #dropgold/ 7j5=:BTC/BTC:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n SjLPs:ETH.FOX-D808EE52D:0x1B9bB0A0A55f30edAA6A03D55190C0Ad72CacaE5:241474682209:ss:0 FjDOUT:9B16F1F08FF0A18A233ADC125C2F22992BCB915282ED375718A0CF1E3CAB0DB0 FjDOUT:23E09844249581E34AFC600F74676C74035825C0A15212AE97CC82B986BE7F11 FjDOUT:C3405794A3B01D9868B96CFBA26E2C1E9950BAAADABEE89BDE6FEACBF01B39A4 IjGREFUND:D3FE02B230362B719DF221E068738DD82244F463594A249384FCAE5568160EFE LjJ=:BNB.BUSD-BD1:bnb16hjkt6tfdwsmy7saqdymh3qy7c24uerep79mts:14478139561:te:0 text/html;charset=utf-8 <title>Basic Doodle Jump HTML Game</title> <meta charset="UTF-8"> align-items: center; justify-content: center; border: 1px solid black; <canvas width="375" height="667" id="game"></canvas> const canvas = document.getElementById('game'); const context = canvas.getContext('2d'); // width and height of each platform and where pM const platformWidth = 65; const platformHeight = 20; const platformStart = canvas.height - 50; const gravity = 0.33; const bounceVelocity = -12.5; // minimum and maximum vertical space between each platform let minPlatformSpace = 15; let maxPlatformSpace = 20; // information about each platform. the first platform starts in the // bottom middle of the screen x: canvas.width / 2 - platformWidth / 2, // get a random numM ber between the min (inclusive) and max (exclusive) function random(min, max) { return Math.random() * (max - min) + min; // fill the initial screen with platforms let y = platformStart; // the next platform can be placed above the previous one with a space // somewhere between the min and max space y -= platformHeight + random(minPlatformSpace, maxPlatformSpace); // a platform can be placed anywhere 25px from the left edge of the canvas // and 25px from the right edge of the canvas M (taking into account platform // however the first few platforms cannot be placed in the center so // that the player will bounce up and down without going up the screen // until they are ready to move x = random(25, canvas.width - 25 - platformWidth); y > canvas.height / 2 && x > canvas.width / 2 - platformWidth * 1.5 && x < canvas.width / 2 + platformWidth / 2 platforms.push({ x, y }); // the doodle jumper x: canvas.width / 2 - 20, y: platformStart - 60, // keep track of player direction and actions let keydown = false; let prevDoodleY = doodle.y; requestAnimationFrame(loop); context.clearRect(0,0,canvas.width,canvas.height); // apply gravity to doodle doodle.dy += gravity; // if doodle reaches the middle of the screen, move the platforms down // instead of doodle up to make it look like doodle is going M if (doodle.y < canvas.height / 2 && doodle.dy < 0) { platforms.forEach(function(platform) { platform.y += -doodle.dy; // add more platforms to the top of the screen as doodle moves up while (platforms[platforms.length - 1].y > 0) { platforms.push({ x: random(25, canvas.width - 25 - platformWidth), y: platforms[platforms.length - 1].y - (platformHeight + random(minPlatformSpace, maxPlatformSpace)) // add a bit to the min/max platform space as the M minPlatformSpace += 0.5; maxPlatformSpace += 0.5; // cap max space maxPlatformSpace = Math.min(maxPlatformSpace, canvas.height / 2); doodle.y += doodle.dy; // only apply drag to horizontal movement if key is not pressed if (playerDir < 0) { doodle.dx += drag; // don't let dx go above 0 if (doodle.dx > 0) { doodle.dx = 0; playerDir = 0; else if (playerDir > 0) { if (doodle.dx < 0) { doodle.dx = 0; playerDir = 0; doodle.x += doodle.dx; // make doodle wrap the screen if (doodle.x + doodle.width < 0) { doodle.x = canvas.width; else if (doodle.x > canvas.width) { doodle.x = -doodle.width; context.fillStyle = 'green'; platforms.forEach(function(platform) { context.fillRect(platform.x, platform.y, platformWidth, platformHeight); // make doodle jump if it collides wiM th a platform from above // doodle is falling doodle.dy > 0 && // doodle was previous above the platform prevDoodleY + doodle.height <= platform.y && // doodle collides with platform // (Axis Aligned Bounding Box [AABB] collision check) doodle.x < platform.x + platformWidth && doodle.x + doodle.width > platform.x && doodle.y < platform.y + platformHeight && doodle.y + doodle.height > platform.y // reset doodle position so it's on tM doodle.y = platform.y - doodle.height; doodle.dy = bounceVelocity; context.fillStyle = 'yellow'; context.fillRect(doodle.x, doodle.y, doodle.width, doodle.height); prevDoodleY = doodle.y; // remove any platforms that have gone offscreen platforms = platforms.filter(function(platform) { return platform.y < canvas.height; // listen to keyboard events to move doodle document.addEventListener('keydown', function(e) { // left arrow keMa if (e.which === 37) { // right arrow key else if (e.which === 39) { document.addEventListener('keyup', function(e) { requestAnimationFrame(loop); %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz &'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, X.X.X.X.X.X.X.X.X.X.X.X.X.X.X.X' c/Foundry USA Pool #dropgold/ Bj@=:ETH.ETH:0x7c5C5cf47141389d3D9fA90dd5250Cb19d5b2064:551599:te:0 EjCs:RUNE:thor1k8dd70sddrwcq3r5xsfvy3veqjylxm4qhs2z6k:35497349738:ss:0 IjGREFUND:ED77D2FB6F9CA99819AE78C4BBD01D4D38861B4E4B2691046A914C9261B42B5A IjGREFUND:F36D2F52AC6FF5BEF334485B58DEA40CACA20440947BBC30E4817C6C4E44F48D <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000"> <title>Bitcoin Face</title> <g style="isolation:isolate"><rect id="Background-5" width="1025" height="1025" style="fill:#f8f3c1" /><g id="Body-8"><rect x="161.21" y="452.66" width="687.79" height="552.16" style="fill:#ba3d11" /><rect x="309.41" y="451.15" width="382.27" height="553.67" style="fill:#542f5f" /><rect x="500.36" y="451.15" width="194.64" height="553.67" style="fill:#172027" /></g><g id="Head-11"><path d="M164.72,490.82C164.72,683.66,3M 17,840,504.85,840S845,683.66,845,490.82" transform="translate(0.28)" style="fill:#f5b659" /><path d="M186,490.82c0,180.79,142.76,327.34,318.87,327.34S823.71,671.61,823.71,490.82" transform="translate(0.28)" style="fill:#ecdea0" /><path d="M228.5,490.82c0,156.68,123.72,283.7,276.35,283.7s276.34-127,276.34-283.7" transform="translate(0.28)" style="fill:#e18d27" /><polygon points="506.79 774.52 503.45 774.52 503.45 510.93 781.47 510.93 781.47 514.36 506.79 514.36 506.79 774.52" style="fill:#ecdea0" /><polygon points="M 695.33 688.51 497.93 508.38 760.74 598.31 759.68 601.56 512.31 516.91 697.55 685.94 695.33 688.51" style="fill:#ecdea0" /><polygon points="609.89 753.4 503.6 513.35 506.64 511.94 612.93 751.98 609.89 753.4" style="fill:#ecdea0" /><polygon points="506.79 774.52 503.45 774.52 503.45 514.36 228.77 514.36 228.77 510.93 506.79 510.93 506.79 774.52" style="fill:#ecdea0" /><polygon points="314.91 688.51 312.69 685.94 497.93 516.91 250.56 601.56 249.5 598.31 512.31 508.38 314.91 688.51" style="fill:#ecdea0" /><polygon poinM ts="400.35 753.4 397.31 751.98 503.6 511.94 506.64 513.35 400.35 753.4" style="fill:#ecdea0" /><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M664.28,601.65A12.63,12.63,0,1,1,676.58,589,12.47,12.47,0,0,1,664.28,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,664.28,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M643,579.83a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,643,579.83ZM643,558a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,643,5M 58Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M738.68,579.83c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.92,10.56,22.92,23.54S751.32,579.83,738.68,579.83Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.58-9,19.58-20.11S749.48,536.18,738.68,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M685.53,536.18a12.63,12.63,0,1,1,12.3-12.63A12.47,12.47,0,0M ,1,685.53,536.18Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.08,9.08,0,0,0,685.53,514.36Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M483.59,732.59c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.93,10.56,22.93,23.54S496.23,732.59,483.59,732.59Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.58-9,19.58-20.11S494.39,688.94,483.59,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:oveM rlay"><path d="M451.7,688.94A12.63,12.63,0,1,1,464,676.32,12.48,12.48,0,0,1,451.7,688.94Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,451.7,667.12Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M387.93,601.65A12.63,12.63,0,1,1,400.23,589,12.48,12.48,0,0,1,387.93,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,387.93,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M324.16,558M a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,324.16,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,324.16,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M260.38,558a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,260.38,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,260.38,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M292.27,645.3c-12.64,0-22.93-10.56-22.93-23.54sM 10.29-23.54,22.93-23.54,22.93,10.56,22.93,23.54S304.91,645.3,292.27,645.3Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.59-9,19.59-20.11S303.07,601.65,292.27,601.65Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M324.16,667.12a12.63,12.63,0,1,1,12.3-12.63A12.48,12.48,0,0,1,324.16,667.12Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,324.16,645.3Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-M mode:overlay"><path d="M600.5,667.12a12.63,12.63,0,1,1,12.3-12.63A12.48,12.48,0,0,1,600.5,667.12Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.08,9.08,0,0,0,600.5,645.3Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M664.28,710.76a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,664.28,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,664.28,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M611M .13,732.59c-12.64,0-22.92-10.56-22.92-23.54s10.28-23.54,22.92-23.54,22.93,10.56,22.93,23.54S623.77,732.59,611.13,732.59Zm0-43.65c-10.8,0-19.58,9-19.58,20.11s8.78,20.11,19.58,20.11,19.59-9,19.59-20.11S621.94,688.94,611.13,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M706.79,645.3a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,706.79,645.3Zm0-21.83a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,706.79,623.47Z" transform="translate(0.28)" style="fill:#aeaM 4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M473,579.83a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,473,579.83ZM473,558a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,473,558Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M430.45,536.18a12.63,12.63,0,1,1,12.29-12.63A12.48,12.48,0,0,1,430.45,536.18Zm0-21.82a9.2,9.2,0,1,0,8.95,9.19A9.09,9.09,0,0,0,430.45,514.36Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.M 52;mix-blend-mode:overlay"><path d="M579.25,558a12.63,12.63,0,1,1,12.3-12.62A12.48,12.48,0,0,1,579.25,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,579.25,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558,601.65A12.63,12.63,0,1,1,570.29,589,12.48,12.48,0,0,1,558,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,558,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M4M 94.22,649.82a17.15,17.15,0,1,1,16.7-17.15A17,17,0,0,1,494.22,649.82Zm0-30.87a13.72,13.72,0,1,0,13.36,13.72A13.56,13.56,0,0,0,494.22,619Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M536.73,710.76A12.63,12.63,0,1,1,549,698.14,12.48,12.48,0,0,1,536.73,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,536.73,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558,758.93a17.15,17.15M ,0,1,1,16.7-17.15A16.95,16.95,0,0,1,558,758.93Zm0-30.86a13.72,13.72,0,1,0,13.36,13.71A13.55,13.55,0,0,0,558,728.07Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M366.67,710.76A12.63,12.63,0,1,1,379,698.14,12.48,12.48,0,0,1,366.67,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,366.67,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><rect x="164.72" y="490.82" width="680.24" height="21.82" transform="translate(1009.97 1003.46) rotaM te(-180)" style="fill:#aea4b3;opacity:0.52;mix-blend-mode:overlay" /><path d="M845.81,507.17C845.81,314.33,693.53,158,505.69,158S165.57,314.33,165.57,507.17" transform="translate(0.28)" style="fill:#f5b659" /><path d="M824.55,507.17c0-180.79-142.76-327.35-318.86-327.35S186.82,326.38,186.82,507.17" transform="translate(0.28)" style="fill:#ecdea0" /><path d="M782,507.17c0-156.69-123.73-283.7-276.35-283.7s-276.35,127-276.35,283.7" transform="translate(0.28)" style="fill:#e18d27" /><polygon points="504.29 223.47 507.63M 223.47 507.63 487.06 229.62 487.06 229.62 483.63 504.29 483.63 504.29 223.47" style="fill:#ecdea0" /><polygon points="315.76 309.48 513.15 489.61 250.35 399.68 251.4 396.43 498.77 481.07 313.54 312.04 315.76 309.48" style="fill:#ecdea0" /><polygon points="401.19 244.58 507.49 484.63 504.44 486.05 398.15 246 401.19 244.58" style="fill:#ecdea0" /><polygon points="504.29 223.47 507.63 223.47 507.63 483.63 782.31 483.63 782.31 487.06 504.29 487.06 504.29 223.47" style="fill:#ecdea0" /><polygon points="696.17 309.48 69M 8.39 312.04 513.15 481.07 760.53 396.43 761.58 399.68 498.77 489.61 696.17 309.48" style="fill:#ecdea0" /><polygon points="610.73 244.58 613.77 246 507.49 486.05 504.44 484.63 610.73 244.58" style="fill:#ecdea0" /><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M346.26,396.34A12.63,12.63,0,1,1,334,409,12.48,12.48,0,0,1,346.26,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,346.26,418.16Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M367.M 51,418.16a12.63,12.63,0,1,1-12.29,12.63A12.48,12.48,0,0,1,367.51,418.16Zm0,21.82a9.2,9.2,0,1,0-8.95-9.19A9.08,9.08,0,0,0,367.51,440Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M271.85,418.16c12.65,0,22.93,10.56,22.93,23.54s-10.28,23.54-22.93,23.54-22.92-10.56-22.92-23.54S259.21,418.16,271.85,418.16Zm0,43.65c10.81,0,19.59-9,19.59-20.11s-8.78-20.11-19.59-20.11-19.58,9-19.58,20.11S261.05,461.81,271.85,461.81Z" transform="translate(0.28)" style="fill:M #aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M325,461.81a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,325,461.81Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,325,483.63Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M527,265.4c12.64,0,22.92,10.56,22.92,23.54S539.59,312.48,527,312.48,504,301.92,504,288.94,514.3,265.4,527,265.4Zm0,43.65c10.8,0,19.58-9,19.58-20.11s-8.78-20.11-19.58-20.11-19.59,9-19.59,20.11S516.15,309.05,527,M 309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558.83,309.05a12.63,12.63,0,1,1-12.3,12.62A12.47,12.47,0,0,1,558.83,309.05Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,558.83,330.87Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M622.61,396.34A12.63,12.63,0,1,1,610.31,409,12.47,12.47,0,0,1,622.61,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,622.61,418.16Z" transform="tranM slate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M686.38,440a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,686.38,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,686.38,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M750.15,440a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,750.15,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,750.15,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" />M </g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M718.26,352.69c12.65,0,22.93,10.56,22.93,23.54s-10.28,23.54-22.93,23.54-22.92-10.56-22.92-23.54S705.62,352.69,718.26,352.69Zm0,43.65c10.81,0,19.59-9,19.59-20.11s-8.78-20.11-19.59-20.11-19.58,9-19.58,20.11S707.46,396.34,718.26,396.34Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M686.38,330.87a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,686.38,330.87Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09M ,0,0,0,686.38,352.69Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M410,330.87a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,410,330.87Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,410,352.69Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M346.26,287.22A12.63,12.63,0,1,1,334,299.85,12.49,12.49,0,0,1,346.26,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,346.26,309.05Z" transform=M "translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M399.4,265.4c12.64,0,22.93,10.56,22.93,23.54S412,312.48,399.4,312.48s-22.93-10.56-22.93-23.54S386.76,265.4,399.4,265.4Zm0,43.65c10.8,0,19.59-9,19.59-20.11s-8.79-20.11-19.59-20.11-19.59,9-19.59,20.11S388.6,309.05,399.4,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M303.74,352.69a12.63,12.63,0,1,1-12.3,12.63A12.48,12.48,0,0,1,303.74,352.69Zm0,21M .82a9.2,9.2,0,1,0-9-9.19A9.09,9.09,0,0,0,303.74,374.51Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M537.58,418.16a12.63,12.63,0,1,1-12.3,12.63A12.47,12.47,0,0,1,537.58,418.16Zm0,21.82a9.2,9.2,0,1,0-9-9.19A9.08,9.08,0,0,0,537.58,440Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M580.09,461.81a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,580.09,461.81Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9M .09,9.09,0,0,0,580.09,483.63Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M431.29,440A12.63,12.63,0,1,1,419,452.61,12.49,12.49,0,0,1,431.29,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,431.29,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M452.54,396.34A12.63,12.63,0,1,1,440.25,409,12.47,12.47,0,0,1,452.54,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,452.54,418.16Z"M transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M516.32,348.17a17.15,17.15,0,1,1-16.7,17.15A16.95,16.95,0,0,1,516.32,348.17Zm0,30.86A13.72,13.72,0,1,0,503,365.32,13.55,13.55,0,0,0,516.32,379Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M473.8,287.22a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,473.8,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,473.8,309.05Z" transform="translaM te(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M452.54,239.06a17.15,17.15,0,1,1-16.7,17.14A17,17,0,0,1,452.54,239.06Zm0,30.86a13.72,13.72,0,1,0-13.36-13.72A13.56,13.56,0,0,0,452.54,269.92Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M643.86,287.22a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,643.86,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,643.86,309.05Z" transform="translate(0.28)" style="fM ill:#aea4b3" /></g><rect x="165.84" y="485.34" width="680.24" height="21.82" style="fill:#aea4b3;opacity:0.52;mix-blend-mode:overlay" /></g><g id="Face-Accessory-1"><circle cx="516.52" cy="942.46" r="62.02" style="fill:#f96020" /><circle cx="516.52" cy="942.46" r="54.28" style="fill:#d74816" /><rect x="487.05" y="879.79" width="35.52" height="96.79" transform="translate(214.46 1954.46) rotate(-135)" style="fill:#ffa520" /><circle cx="510.95" cy="942.07" r="66.36" style="fill:#f96020" /><circle cx="510.94" cy="942.0M 7" r="58.08" style="fill:#d74816" /><g id="_04CbBX.tif" data-name="04CbBX.tif"><path d="M499.36,875.6h5.23a5.28,5.28,0,0,0,1.13.08c1.51.13,3,.28,4.52.52a52.15,52.15,0,0,1,10.43,2.72,53.73,53.73,0,0,1,32.17,33.55,52.41,52.41,0,0,1,2.21,10c.15,1.22.26,2.45.31,3.67a1.63,1.63,0,0,0,.08.82v4.31a1.52,1.52,0,0,0-.08.82,46.85,46.85,0,0,1-.52,5.14,52,52,0,0,1-5.39,16.51,53.57,53.57,0,0,1-43.89,28.75,50.62,50.62,0,0,1-8-.07,52.07,52.07,0,0,1-10.21-1.83,53.13,53.13,0,0,1-29.73-21.46,53,53,0,0,1-9.05-25.57,17,17,0,0,0-.16-1.88M c0-.61,0-1.22,0-1.83,0-1.1,0-2.2,0-3.3a.77.77,0,0,0,.07-.4,53.09,53.09,0,0,1,5.25-20.37,53.67,53.67,0,0,1,35.15-28.6,54.71,54.71,0,0,1,9.34-1.48A8.57,8.57,0,0,0,499.36,875.6Zm-42,53.5a44.61,44.61,0,1,0,44.59-44.62A44.59,44.59,0,0,0,457.33,929.1Z" transform="translate(9.03 13)" style="fill:#ff9317" /><path d="M515.36,927.51a21.52,21.52,0,0,1,5.48,1.69,11.83,11.83,0,0,1,5.27,4.7,12,12,0,0,1,1.52,4.64,17.76,17.76,0,0,1-.27,6.21,10.87,10.87,0,0,1-6.8,8,25.21,25.21,0,0,1-5.77,1.53c-1.34.21-2.68.35-4,.44-.47,0-.47,0-.47.M 51v7.94c0,.5,0,.51-.51.51H504c-.56,0-.53,0-.53-.53v-7.68c0-.64.05-.57-.58-.57h-5.8c-.46,0-.46,0-.47.48v7.73c0,.59,0,.57-.54.57h-5.74c-.55,0-.55,0-.55-.57,0-2.51,0-5,0-7.53,0-.14,0-.27,0-.41s-.06-.29-.26-.27H476.43c-.24,0-.34-.06-.34-.31,0-1.9,0-3.79,0-5.69,0-.27.11-.35.36-.35,1.18,0,2.36,0,3.54,0a10.18,10.18,0,0,0,2.13-.27,1.92,1.92,0,0,0,1.55-1.57,11.89,11.89,0,0,0,.29-2.85V921.52c0-2.47,0-4.95,0-7.43a13.56,13.56,0,0,0-.27-2.69,2,2,0,0,0-1.71-1.68,13.39,13.39,0,0,0-2.75-.24h-2.66c-.47,0-.47,0-.47-.49,0-1.76,0-3.51M ,0-5.27,0-.6-.06-.56.54-.57h12.55c.57,0,.57,0,.58-.54v-7.89c0-.2.09-.28.28-.26h6c.61,0,.56-.06.56.54,0,2.51,0,5,0,7.53,0,.7-.05.62.64.62H503c.46,0,.46,0,.47-.49v-7.94c0-.2.07-.28.27-.26h6c.59,0,.57-.09.57.58v7.84c0,.39,0,.39.39.42a32.44,32.44,0,0,1,6.11,1,17.64,17.64,0,0,1,3.2,1.2,9.17,9.17,0,0,1,5,6.06,14.22,14.22,0,0,1,.12,7.24,10.31,10.31,0,0,1-4.17,6,18.31,18.31,0,0,1-5.34,2.53C515.55,927.36,515.43,927.34,515.36,927.51Zm-18.76,12.4c0,2.77,0,5.53,0,8.3,0,.44,0,.45.44.45h3a44,44,0,0,0,6.39-.31,15,15,0,0,0,3.66-.9M 3,6.31,6.31,0,0,0,3.8-4.16,12,12,0,0,0,.26-6,6.32,6.32,0,0,0-2.8-4.24A9.11,9.11,0,0,0,508.7,932a23.93,23.93,0,0,0-4.61-.65c-2.39-.14-4.78,0-7.17-.08-.26,0-.34.1-.32.33s0,.24,0,.36Zm0-22.76v4.25c0,1.05,0,2.09,0,3.13,0,.25,0,.37.34.37a68.58,68.58,0,0,0,7.72-.23,14.47,14.47,0,0,0,3.67-.89,5.57,5.57,0,0,0,3.45-4A11.75,11.75,0,0,0,512,915a5.49,5.49,0,0,0-3.83-4.58,18.25,18.25,0,0,0-5-.88c-2-.1-4,0-6,0h-.21c-.17,0-.25.08-.24.25s0,.27,0,.41Z" transform="translate(9.03 13)" style="fill:#ff9317" /></g><circle cx="459.8" cy=M "882.7" r="13.97" style="fill:#ff9317" /><circle cx="438.84" cy="861.74" r="13.97" style="fill:#ff9317" /><circle cx="417.89" cy="840.79" r="13.97" style="fill:#ff9317" /><circle cx="396.93" cy="819.83" r="13.97" style="fill:#ff9317" /><circle cx="564.57" cy="882.7" r="13.97" style="fill:#f96020" /><circle cx="585.53" cy="861.74" r="13.97" style="fill:#f96020" /><circle cx="606.48" cy="840.79" r="13.97" style="fill:#f96020" /><circle cx="627.44" cy="819.83" r="13.97" style="fill:#f96020" /></g><g id="Earrings-1"><cM ircle cx="141.68" cy="475.44" r="61.24" style="fill:#f26227" /><circle cx="141.68" cy="475.44" r="53.6" style="fill:#d74b27" /><g><path d="M134.36,420.84h4.82a6.06,6.06,0,0,0,1,.07c1.4.12,2.79.26,4.18.48A48.47,48.47,0,0,1,154,423.9a49.56,49.56,0,0,1,29.69,31,47.44,47.44,0,0,1,2,9.2c.13,1.13.24,2.25.28,3.39a1.48,1.48,0,0,0,.07.75v4a1.37,1.37,0,0,0-.07.75,42.22,42.22,0,0,1-.48,4.75,47.88,47.88,0,0,1-5,15.23,49.3,49.3,0,0,1-47.87,26.48,47.84,47.84,0,0,1-9.43-1.7,49.07,49.07,0,0,1-27.44-19.8,49.09,49.09,0,0,1-8.35-23.6M ,12.49,12.49,0,0,0-.15-1.73c0-.57,0-1.13,0-1.69,0-1,0-2,0-3.05a.57.57,0,0,0,.07-.36,47.07,47.07,0,0,1,.76-6.38,48.23,48.23,0,0,1,4.09-12.43,49.32,49.32,0,0,1,41.06-27.75A9.08,9.08,0,0,0,134.36,420.84ZM95.57,470.21A41.17,41.17,0,1,0,136.72,429,41.12,41.12,0,0,0,95.57,470.21Z" transform="translate(4.96 5.26)" style="fill:#f79421" /><path d="M149.13,468.75a19.6,19.6,0,0,1,5.05,1.56,10.88,10.88,0,0,1,4.87,4.33,11.08,11.08,0,0,1,1.4,4.28,16.24,16.24,0,0,1-.25,5.73,10.06,10.06,0,0,1-6.28,7.4,23.28,23.28,0,0,1-5.33,1.41c-M 1.23.19-2.46.33-3.71.4-.43,0-.44,0-.44.48v7.33c0,.46,0,.46-.47.46h-5.35c-.52,0-.49,0-.49-.48v-7.09c0-.59.05-.53-.54-.53h-5.34c-.43,0-.43,0-.43.44,0,2.38,0,4.76,0,7.14,0,.55.05.52-.5.52H126c-.5,0-.51,0-.51-.52v-7.33c0-.17-.06-.27-.24-.25s-.26,0-.38,0H113.19c-.21,0-.31,0-.31-.29q0-2.62,0-5.25c0-.25.1-.32.34-.31h3.26a9.33,9.33,0,0,0,2-.26,1.76,1.76,0,0,0,1.43-1.45,11,11,0,0,0,.26-2.63q0-10.31,0-20.61c0-2.29,0-4.57,0-6.86a11.52,11.52,0,0,0-.24-2.48,1.88,1.88,0,0,0-1.58-1.55,12.77,12.77,0,0,0-2.54-.23c-.82,0-1.64,0-2.46M ,0-.43,0-.43,0-.43-.45v-4.87c0-.56,0-.52.5-.52H125c.52,0,.52,0,.52-.51v-6.9c0-.12,0-.25,0-.38s.09-.25.26-.24h5.54c.56,0,.51-.06.51.5,0,2.32,0,4.63,0,6.95,0,.65,0,.58.58.58h5.3c.43,0,.43,0,.43-.46v-7c0-.11,0-.22,0-.33s.07-.26.25-.25h5.54c.55,0,.52-.08.52.54V446c0,.36,0,.35.37.38a30.6,30.6,0,0,1,5.63.91,16.49,16.49,0,0,1,3,1.11,8.5,8.5,0,0,1,4.65,5.6,13.15,13.15,0,0,1,.11,6.68,9.54,9.54,0,0,1-3.85,5.55,17,17,0,0,1-4.93,2.34C149.3,468.61,149.19,468.58,149.13,468.75Zm-17.31,11.44c0,2.55,0,5.1,0,7.66,0,.41,0,.41.41.41H1M 35a40.14,40.14,0,0,0,5.9-.28,14.22,14.22,0,0,0,3.38-.86,5.83,5.83,0,0,0,3.5-3.84,11,11,0,0,0,.24-5.51,5.78,5.78,0,0,0-2.58-3.91,8.31,8.31,0,0,0-2.49-1,21.66,21.66,0,0,0-4.25-.6c-2.2-.13-4.41,0-6.62-.07-.23,0-.31.09-.29.3s0,.22,0,.33Q131.81,476.5,131.82,480.19Zm0-21v3.92q0,1.44,0,2.88c0,.23.05.35.31.34a62.82,62.82,0,0,0,7.13-.2,13.67,13.67,0,0,0,3.39-.83,5.13,5.13,0,0,0,3.18-3.67,10.6,10.6,0,0,0,.15-4.45,5,5,0,0,0-3.52-4.22,16.7,16.7,0,0,0-4.64-.82c-1.86-.09-3.72,0-5.58,0H132a.2.2,0,0,0-.22.23v.38Q131.81,456,131.81,M 459.19Z" transform="translate(4.96 5.26)" style="fill:#f79421" /></g><circle cx="897.68" cy="478.42" r="61.24" style="fill:#f26227" /><circle cx="897.68" cy="478.42" r="53.6" style="fill:#d74b27" /><g><path d="M890.36,423.81h4.82a5.2,5.2,0,0,0,1,.07c1.4.12,2.79.27,4.18.48a48.5,48.5,0,0,1,9.62,2.52,49.52,49.52,0,0,1,29.69,31,47.6,47.6,0,0,1,2,9.2c.13,1.13.24,2.26.28,3.39a1.52,1.52,0,0,0,.07.76v4a1.4,1.4,0,0,0-.07.76,41.81,41.81,0,0,1-.48,4.74,47.88,47.88,0,0,1-5,15.23,49.44,49.44,0,0,1-40.51,26.54,47.59,47.59,0,0,1-M 7.36-.06,48.57,48.57,0,0,1-9.43-1.69,49.12,49.12,0,0,1-27.44-19.8,49.17,49.17,0,0,1-8.35-23.6,12.63,12.63,0,0,0-.15-1.74c0-.56,0-1.12,0-1.69,0-1,0-2,0-3a.62.62,0,0,0,.07-.37,47.11,47.11,0,0,1,.76-6.37,48.1,48.1,0,0,1,4.09-12.43,49.49,49.49,0,0,1,32.44-26.4,50.07,50.07,0,0,1,8.62-1.36A7.27,7.27,0,0,0,890.36,423.81Zm-38.79,49.37A41.17,41.17,0,1,0,892.72,432,41.14,41.14,0,0,0,851.57,473.18Z" transform="translate(4.96 5.26)" style="fill:#f79421" /><path d="M905.13,471.72a19.6,19.6,0,0,1,5,1.56,10.85,10.85,0,0,1,4.87,4.M 34,11,11,0,0,1,1.4,4.28,16.25,16.25,0,0,1-.25,5.73,10,10,0,0,1-6.28,7.39,23.28,23.28,0,0,1-5.33,1.41c-1.23.19-2.46.33-3.71.41-.43,0-.44,0-.44.47v7.33c0,.46,0,.47-.47.47h-5.35c-.52,0-.49,0-.49-.49v-7.09c0-.59,0-.53-.54-.53h-5.34c-.43,0-.43,0-.43.45,0,2.38,0,4.76,0,7.14,0,.54.05.52-.5.52H882c-.5,0-.51,0-.51-.53v-7.33c0-.17-.06-.26-.24-.25H869.19c-.21,0-.31-.05-.31-.28q0-2.62,0-5.25c0-.26.1-.32.34-.32h3.26a9.33,9.33,0,0,0,2-.26,1.76,1.76,0,0,0,1.43-1.45,11,11,0,0,0,.26-2.62q0-10.32,0-20.62c0-2.28,0-4.57,0-6.85a11.55,1M 1.55,0,0,0-.24-2.49,1.88,1.88,0,0,0-1.58-1.55,12.77,12.77,0,0,0-2.54-.23h-2.46c-.43,0-.43,0-.43-.45v-4.87c0-.55,0-.52.5-.52H881c.52,0,.52,0,.52-.5v-6.91c0-.12,0-.25,0-.37s.09-.26.26-.25h5.54c.56,0,.51-.05.51.5,0,2.32,0,4.64,0,6.95,0,.65-.05.58.58.58h5.3c.43,0,.43,0,.43-.45v-7c0-.11,0-.22,0-.33s.07-.26.25-.25h5.54c.55,0,.52-.07.52.55V449c0,.36,0,.36.37.38a30.62,30.62,0,0,1,5.63.92,16.45,16.45,0,0,1,3,1.1,8.5,8.5,0,0,1,4.65,5.6,13.19,13.19,0,0,1,.11,6.69,9.49,9.49,0,0,1-3.85,5.54,16.79,16.79,0,0,1-4.93,2.34C905.3,471M .58,905.19,471.56,905.13,471.72Zm-17.31,11.44c0,2.55,0,5.11,0,7.66,0,.41,0,.41.41.41H891a40.14,40.14,0,0,0,5.9-.29,14.2,14.2,0,0,0,3.38-.85,5.85,5.85,0,0,0,3.5-3.85,11,11,0,0,0,.24-5.51,5.78,5.78,0,0,0-2.58-3.91,8.31,8.31,0,0,0-2.49-1,23.27,23.27,0,0,0-4.25-.6c-2.2-.13-4.41,0-6.62-.07-.23,0-.31.09-.29.31s0,.22,0,.33C887.81,478.25,887.81,480.7,887.82,483.16Zm0-21v3.92c0,1,0,1.92,0,2.89,0,.22.05.34.31.33a62.82,62.82,0,0,0,7.13-.2,13.67,13.67,0,0,0,3.39-.83,5.11,5.11,0,0,0,3.18-3.66,10.65,10.65,0,0,0,.15-4.46,5,5,0,0,M 0-3.52-4.22,16.68,16.68,0,0,0-4.64-.81c-1.86-.1-3.72,0-5.58-.05H888c-.15,0-.23.07-.22.23v.38Q887.8,458.92,887.81,462.16Z" transform="translate(4.96 5.26)" style="fill:#f79421" /></g></g><g id="Ears-6"><path d="M305.64,230v64A22.69,22.69,0,0,1,283,316.61H262.15a97.69,97.69,0,0,0-37.57,7.51h0a87.1,87.1,0,0,1-112.14-43L108,271.88a86.58,86.58,0,0,1,44.06-116.81h0A86.61,86.61,0,0,1,208.5,151l89.26,23.81H187.46a39.39,39.39,0,0,0-39.39,39.39h0a39.4,39.4,0,0,0,39.39,39.39h4.85A39.42,39.42,0,0,0,212.58,248l40.76-24.46a42.8,M 42.8,0,0,1,52.3,6.44Z" transform="translate(0.34)" style="fill:#542f5f" /><path d="M722.64,230.16v64a22.68,22.68,0,0,0,22.68,22.68h20.81a97.64,97.64,0,0,1,37.56,7.51h0a87.11,87.11,0,0,0,112.15-42.95l4.42-9.29a86.58,86.58,0,0,0-44.07-116.81h0a86.58,86.58,0,0,0-56.41-4.07L730.51,175h110.3A39.39,39.39,0,0,1,880.2,214.4h0a39.38,39.38,0,0,1-39.39,39.39H836a39.42,39.42,0,0,1-20.27-5.61l-40.77-24.46a42.8,42.8,0,0,0-52.29,6.44Z" transform="translate(0.34)" style="fill:#111c0d" /></g><g id="Mouth-1"><path d="M504.57,545.25HM 236.78c-25.78,0-40.38,29.55-24.72,50l35.83,46.85a64.57,64.57,0,0,0,51.31,25.36H504.57" transform="translate(0.28 0)" style="fill:#883a62" /><path d="M483,545.25H775.4c25.78,0,40.38,29.55,24.72,50l-35.83,46.85A64.57,64.57,0,0,1,713,667.49H483" transform="translate(0.28 0)" style="fill:#883a62" /><rect x="267.55" y="570.35" width="474.59" height="64.72" style="fill:#542f5f" /><rect x="505.9" y="556.65" width="247.44" height="37.12" style="fill:#172027" /><path d="M712.79,556.31h0a15.47,15.47,0,0,1,15.47,15.47v21.65a0M ,0,0,0,1,0,0H697.33a0,0,0,0,1,0,0V571.78A15.47,15.47,0,0,1,712.79,556.31Z" style="fill:#f489ae" /><rect x="505.56" y="624.36" width="204.14" height="24.74" style="fill:#172027" /><rect x="654.03" y="624.36" width="30.93" height="24.74" style="fill:#f489ae" /><path d="M691.21,594.11v55.34h37.12A52.45,52.45,0,0,0,709.23,609Z" transform="translate(0.28 0)" style="fill:#f489ae" /><path d="M740.7,575.21v61.86l13.35-11.13a66,66,0,0,0,23.77-50.73h0a18.56,18.56,0,0,0-18.56-18.56h0A18.56,18.56,0,0,0,740.7,575.21Z" transformM ="translate(0.28 0)" style="fill:#f489ae" /><rect x="257.84" y="556.31" width="247.44" height="37.12" transform="translate(763.4 1149.74) rotate(-180)" style="fill:#172027" /><path d="M282.93,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556a0,0,0,0,1,0,0Z" transform="translate(597.06 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="301.49" y="624.01" width="204.14" height="24.74" transform="translate(807.39 1272.77) rotate(-180)" style="fill:#172027" /><rect x="3M 26.23" y="624.01" width="30.93" height="24.74" transform="translate(683.67 1272.77) rotate(-180)" style="fill:#f9c1c0" /><path d="M319.7,593.77V649.1H282.58a52.47,52.47,0,0,1,19.1-40.48Z" transform="translate(0.28 0)" style="fill:#f9c1c0" /><path d="M270.21,574.87v61.86L256.86,625.6a66,66,0,0,1-23.77-50.73h0a18.56,18.56,0,0,1,18.56-18.56h0A18.56,18.56,0,0,1,270.21,574.87Z" transform="translate(0.28 0)" style="fill:#f9c1c0" /><path d="M329.67,556H360.6a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,M 0,0,1-15.47-15.47V556A0,0,0,0,1,329.67,556Z" transform="translate(690.54 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="372.97" y="624.01" width="30.93" height="24.74" transform="translate(777.15 1272.77) rotate(-180)" style="fill:#f9c1c0" /><path d="M375.14,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556a0,0,0,0,1,0,0Z" transform="translate(781.48 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="418.44" y="624.01" width="30.93" height="24.74" transform=M "translate(868.09 1272.77) rotate(-180)" style="fill:#f9c1c0" /><path d="M423.15,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556A0,0,0,0,1,423.15,556Z" transform="translate(877.5 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="466.45" y="624.01" width="30.93" height="24.74" transform="translate(964.1 1272.77) rotate(-180)" style="fill:#f9c1c0" /><path d="M473.64,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556A0,0,M 0,0,1,473.64,556Z" transform="translate(978.48 1149.05) rotate(-180)" style="fill:#f9c1c0" /><rect x="513.19" y="624.7" width="30.93" height="24.74" transform="translate(1057.58 1274.15) rotate(-180)" style="fill:#f489ae" /><rect x="559.93" y="624.7" width="30.93" height="24.74" transform="translate(1151.06 1274.15) rotate(-180)" style="fill:#f489ae" /><rect x="605.4" y="624.7" width="30.93" height="24.74" transform="translate(1242 1274.15) rotate(-180)" style="fill:#f489ae" /><rect x="653.41" y="624.7" width="30.9M 3" height="24.74" transform="translate(1338.02 1274.15) rotate(-180)" style="fill:#f489ae" /><path d="M509.43,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556a0,0,0,0,1,0,0Z" transform="translate(1050.07 1149.05) rotate(-180)" style="fill:#f489ae" /><path d="M554.9,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556A0,0,0,0,1,554.9,556Z" transform="translate(1141.02 1149.05) rotate(-180)" style="fill:#f489ae" /><path d="M602.M 91,556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556A0,0,0,0,1,602.91,556Z" transform="translate(1237.03 1149.05) rotate(-180)" style="fill:#f489ae" /><path d="M653.41556h30.93a0,0,0,0,1,0,0v21.65a15.47,15.47,0,0,1-15.47,15.47h0a15.47,15.47,0,0,1-15.47-15.47V556a0,0,0,0,1,0,0Z" transform="translate(1338.02 1149.05) rotate(-180)" style="fill:#f489ae" /></g><g id="Eyebrows-1">undefined</g><g id="Glasses-4">undefined</g><g id="Eyes-3"><rect x="568.09" y="366.84" width="201M .78" height="108.65" rx="51.21" transform="translate(-105 257.04) rotate(-19.91)" style="fill:#3d2f39" /><circle cx="645.54" cy="433.1" r="46.56" style="fill:#eb6447" /><rect x="639.33" y="390.29" width="15.52" height="77.61" rx="7.32" transform="translate(-109.01 250.06) rotate(-19.91)" style="fill:#3d2f39" /><rect x="241.28" y="368.37" width="201.78" height="108.65" rx="51.21" transform="translate(518.36 940.65) rotate(-160.09)" style="fill:#80191b" /><circle cx="362.51" cy="434.62" r="46.56" style="fill:#eb6447"M /><rect x="356.3" y="391.82" width="15.52" height="77.61" rx="7.32" transform="translate(558.13 963.49) rotate(-160.09)" style="fill:#3d2f39" /></g><g id="Nose-2"><circle cx="478.44" cy="516.44" r="9.88" style="fill:#303636" /><path d="M503,519.73h-3.65a24.52,24.52,0,0,0-49,0H446.7a28.17,28.17,0,0,1,56.34,0Z" transform="translate(0.28 0)" style="fill:#efe1da" /><circle cx="535.24" cy="515.88" r="9.88" style="fill:#303636" /><path d="M510.08,519.17h3.66a24.52,24.52,0,0,1,49,0h3.66a28.18,28.18,0,0,0-56.35,0Z" transfMG orm="translate(0.28 0)" style="fill:#efe1da" /></g><g id="Hat-5"><path d="M704.39,126H507.13l12.77,10.22A138.53,138.53,0,0,0,704.39,126Z" transform="translate(0.28)" style="fill:#ffa520" /><path d="M703.55,126H506.29l12.77-10.22A138.53,138.53,0,0,1,703.55,126Z" transform="translate(0.28)" style="fill:#b98d22" /></g></g></svg>h! c/Foundry USA Pool #dropgold/ SjLPs:ETH.USDC-E3606EB48:0x834bf675dAf52B5D6fD953a57c83e824521E5ab6:21579472667:ss:0 JjH=:BNB.BUSD-BD1:bnb1kcx0e9l8a0d0ckvfmd7pf4l4y9qspk9ye285pp:224477524:te:0 c/Foundry USA Pool #dropgold/ ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, <svg viewBox="0 0 1287 1287" xmlns="http://www.w3.org/2000/svg"><g transform="translate(664.886 1117.481)"><path d="M165.818 104.54c-11.243 45.096-56.917 72.54-102.017 61.294-45.082-11.243-72.527-56.92-61.279-102.012 11.238-45.1 56.912-72.547 102-61.305 45.097 11.243 72.54 56.925 61.296 102.023z" fill="#F7931A"/><path d="M121.27 72.185c1.676-11.201-6.852-17.223-18.514-21.24l3.783-15.172-9.236-2.302-3.682 14.773a386.353 386.353 0 0 0-7.4-1.741l3.709-14.87-9.23-2.303-3.786 15.168c-2.01-.458-3.983-.91-5.898-1.386l.01-M .047-12.736-3.18-2.457 9.864s6.852 1.57 6.708 1.667c3.74.934 4.416 3.41 4.303 5.372L56.48 98.353c-.458 1.137-1.618 2.841-4.233 2.194.093.134-6.713-1.676-6.713-1.676l-4.585 10.573 12.02 2.996c2.235.56 4.426 1.147 6.583 1.699l-3.822 15.347 9.226 2.301 3.785-15.183a351.26 351.26 0 0 0 7.36 1.91l-3.772 15.112 9.236 2.302 3.822-15.318c15.749 2.98 27.592 1.778 32.577-12.466 4.016-11.47-.2-18.085-8.487-22.399 6.035-1.392 10.58-5.361 11.793-13.56zm-21.102 29.59c-2.854 11.47-22.165 5.27-28.426 3.715l5.072-20.332c6.26 1.563 M 26.337 4.656 23.354 16.618zm2.857-29.756c-2.604 10.433-18.677 5.132-23.89 3.833l4.597-18.44c5.214 1.299 22.005 3.724 19.293 14.607z" fill="#FFF"/></g><path d="M203.138 300.399v-69.033H79.068v-47.384h100.653v-69.034H79.068V69.607h124.07V.573L9.638.982v299.417h193.5zm105.171 0V.165h-69.43v300.234h69.43zm110.512 0v-174.83l98.598 174.83H592.6V.573h-69.43v175.239L424.984 1.39h-75.591v299.009h69.43zm-288.81 328.056c15.611-.273 30.332-3.472 44.163-9.6 13.832-6.127 25.882-14.365 36.153-24.713 10.27-10.348 18.35-22.466 24.2M 39-36.355 5.888-13.888 8.696-28.593 8.422-44.116v-185.45h-69.43V514.08c0 6.263-1.095 12.118-3.286 17.564-2.191 5.447-5.341 10.213-9.45 14.297-4.108 4.085-8.9 7.353-14.378 9.804-5.478 2.45-11.23 3.676-17.255 3.676-12.325 0-23.006-4.22-32.044-12.663-9.038-8.986-13.558-19.47-13.558-31.453V328.22h-69.43v187.494c.275 15.522 3.356 30.091 9.245 43.707 5.888 13.616 14.31 25.87 25.265 36.764 10.682 10.348 22.87 18.313 36.564 23.896 13.694 5.582 28.21 8.374 43.547 8.374h1.233zm223.9 0v-174.83l98.598 174.83h75.18V328.629H458.M 26v175.239l-98.187-174.422h-75.592v299.009h69.43zm271.966 0c20.541-1.09 39.576-5.65 57.105-13.684 17.528-8.034 32.797-18.723 45.807-32.066 13.01-13.344 23.212-28.798 30.606-46.363 7.395-17.565 11.093-36.423 11.093-56.575 0-20.424-3.766-39.623-11.298-57.596s-17.802-33.768-30.812-47.384c-13.01-13.616-28.347-24.645-46.012-33.087-17.666-8.442-36.495-13.207-56.489-14.297v-.408h-69.43v301.46h69.43zm0-69.442V396.437c10.682 1.09 20.541 4.017 29.58 8.782 9.038 4.766 16.98 10.893 23.827 18.382 6.848 7.489 12.188 16.067 16.02M 3 25.734 3.834 9.668 5.751 19.812 5.751 30.432 0 10.348-1.917 20.152-5.751 29.41-3.835 9.26-9.107 17.361-15.817 24.306-6.71 6.944-14.653 12.663-23.828 17.156-9.175 4.493-19.103 7.284-29.785 8.374zM240.934 956.51v-69.033h-124.48l126.123-232.835-225.954-.408v69.442h109.69L.19 956.51h240.744zm179.12 0 34.509-150.321 39.85 150.321h68.608l89.149-301.868h-72.716l-48.888 166.252-44.37-166.252H418l-38.206 167.07-55.873-167.07h-73.127L351.035 956.51h69.019zm293.74 0 12.325-36.354h94.9l11.914 36.354h73.127l-97.366-299.826h-6M 5.732L640.256 956.51h73.538zm85.04-105.388h-48.888l25.06-72.71 23.829 72.71zM996.032 956.51V781.68l98.598 174.83h75.181V656.684h-69.43v175.24L1002.194 657.5h-75.592v299.01h69.43zm-755.097 328.056v-69.033h-124.48l126.123-232.835-225.954-.409v69.442h109.69L.19 1284.566h240.744zm112.155 0V984.332h-69.43v300.234h69.43zm168.85.409c7.942 0 15.953-.613 24.033-1.838 8.08-1.226 15.953-3.2 23.622-5.923v7.352h69.84V1116.68H509.203v69.033h60.391v15.114c-7.943 5.72-16.57 9.804-25.882 12.255-9.312 2.45-18.693 3.336-28.142 2.655-M 9.449-.68-18.624-2.996-27.525-6.944-8.901-3.949-16.912-9.463-24.033-16.544-7.943-7.897-13.968-16.884-18.076-26.96-4.109-10.076-6.163-20.288-6.163-30.636 0-10.348 2.054-20.56 6.163-30.636 4.108-10.076 10.133-19.062 18.076-26.96 7.943-7.897 16.98-13.82 27.114-17.769 10.134-3.948 20.405-5.923 30.812-5.923 10.408 0 20.679 1.975 30.812 5.923 10.134 3.949 19.172 9.872 27.115 17.77l49.299-49.019c-14.79-14.705-31.497-25.734-50.121-33.087-18.624-7.352-37.659-11.029-57.105-11.029-19.445 0-38.48 3.677-57.104 11.03-18.625 7.35M 2-35.331 18.381-50.121 33.086-14.79 14.706-25.814 31.317-33.072 49.835-7.258 18.518-10.887 37.444-10.887 56.78 0 19.334 3.63 38.192 10.887 56.574 7.258 18.382 18.282 34.925 33.072 49.63 14.79 14.706 31.496 25.735 50.12 33.088 18.625 7.352 37.66 11.029 57.105 11.029z"/></svg> ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, 779NSsssu777Sssu777Ssssssu7Su7Sssu7 5sE4SE4SE4SE4SE4SE4SE4SE4S YYYYYYY[++++++++++xuP. MjK=:BNB.BUSD-BD1:bnb1z2kyzy0aapxpu59wxd7gm6pgdwaetl2nxgfk7y:585682793830:t:30 4j2DC-L5:xvy+CDsUGhuDNIBjfx9Ga0czIayXMNvLfgIC1yT+ki0= Bj@=:BNB.BNB:bnb1gjrduftzncf80xp947ehmpp3ldse9gf4ajcmr8:330621:te:0 CjA=:BNB.BNB:bnb1lp2c45aa6zdqdevtdjy5eg89t4zfw4tjlke8tj:2894238:te:0 MjK=:BNB.BUSD-BD1:bnb1nxx047muxr57qhrgxh3q8nnalfzzjcn2zp9j3q:184696061056:te:0 FjDOUT:94458AD0A3EE8ED38F11CE5C794E4F3A438B4EB144D031DF292B9DC5994F7080 6j4ion:2.QmYY7sjXV23CpAdCatTFRbQABJq6X6iheEAWd6oGaqUx2E IjG=:THOR.RUNE:thor104ep0k5pjeame62zuahfjxlqfh323uenx08k0e:5055581576:t:30HO CjA=:BNB.BNB:bnb1r5atp9qhf3ye7ant26ujurv0xamklx0t2seyud:3157523:te:0 FjDOUT:8375162D8627AFD60BA3D79A966F9BC36C0AA80B903689463D1637A4C3F60C19 FjDOUT:ED27E52140C8D63CB28E0C6A552E861F42A921AD210111953B356596B81A1C60 GjE=:BNB.BTCB-1DE:bnb14aaca79pjxk739wvgshj4xpw2lshht6mp35as3:136669:te:0 FjDOUT:71C04475D3E773ABF5D8DC3345DC3966E2EB0D2581C94743FB42115F277E7309 FjDOUT:DB0A69453C456B94E445304B0868D0F9979ACA77184BA612EFACCADB95B9AC5F FjDOUT:88639A08BFDEFA527B9D5C87E35FDEE0860DBCF56E1030F92A925C235F8FC917 FjDOUT:C0A54E71C08D00B9025287FA51D7F6A32A5A33EA25BAD1BF6D2CDA60F2FA31CF Bj@=:ETH.ETH:0x2dc0601A0465f924CDFdf3Bd8f0d9bD13bc461c0:632131:te:0 FjDOUT:ACBA1F4263EB038B4B84A44EA8E0C4CB7A7BCF6AB4D5C1F8F25F01154833BF87 CjA=:ETH.ETH:0x57b558E7068FA48Abe1d5CE686d0204CAa8b5d0d:4599891:te:0 c/Foundry USA Pool #dropgold/ EjC=:ETH.ETH:0x209053265e81db305151f9ec4f3b1ddb6cd6a109:140246830:t:30 FjDOUT:7143897A0C0056E702E689D73B87CE32FA01E5DC6115BF45FE3CB12B4385D066 FjDOUT:72FA33EC1B338AECBE3B217B285119CD5ACF09795C80AB02100F1F3B866CB068 9j7+:BTC/BTC::bc1q3f787hr38pmal87yxtpq8tng09q60ljjqqd759:0 IjGREFUND:F70F4F2F24AC5D7AF736C6C5DAE30679EA42ED811269EA451B01EB9E83C3C219 %j#Voglio Apple Watch Ultra in titanio DjB=:ETH.ETH:0xf1Aa42bcD5e0381f765D223bE36B951F2705fcbc:44511314:te:0 FjDOUT:7DE1612E88A9000A24ACBB3220ECD0FF8FF46B54F701EBA6F7F73248254A81EA IjGREFUND:82181A63D05B693E7C3835536FE95FB2B57FC85FD3E85EB8DFCC0EC7E6D42D46 9j7+:BTC/BTC::bc1q3f787hr38pmal87yxtpq8tng09q60ljjqqd759:0 FjDOUT:1DEB996AFF135B4A76AB5066A9CB16650E2410B00BA00AEB85C36CF6DCDD26D7 FjDOUT:35D76ED1C5A0DC4896C094632211ACF685DC111DC30910E003BC732D72CE0A4F c/Foundry USA Pool #dropgold/ IjGREFUND:779A24D38D48B64BFD74D927B5BF0EAABC83C31A00AC3ED12BB44E539C0229C4 FjDOUT:2E77AB172488F5E91560654EC822E4FBEC601AA6C50417C476B6310E607BFD15 FjDOUT:C6C381E5A06848D05B0ED6FB50F1B90B56A16C8A7E2CB4FD514752CC18DB7906 FjDOUT:5B999C63D1BB7CA12DADC47C10D0AA1AEFC4A963EE57674C6DEC8A84F46E35BD FjDOUT:B11EBD2B75450CA79BAC6BCA4136A6BD6AB3CD44DAA564FCFEA8FEB06BB97DEE FjDOUT:441928A2460851F1F22C07CE6BD4693784B6E7F2158FBAF6E04F231AE21822B1 FjDOUT:A16F258CDEFA98E20DB8C4BC138EFC8B1F29460DD5D3130ED889D8BBC67D5447 FjDOUT:D914E0688C17425B824AB8753D36B2973312357B201ECAE49FBE36FF4899DFB4 Aj?=:BNB.BNB:bnb1d5ewawcrmclzce38uacd6r9xmsjd28yyuykwnn:14775:te:0 CjA=:BNB.BNB:bnb18d42fg2uvw4vd66p664x9l7ftcv3y3hqw9rgf7:7696858:te:0 Copyright Apple Inc., 2018 %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz &'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz c/Foundry USA Pool #dropgold/ 6j4ion:2.Qmbsm8fynSG5bKoGa79f3x2zbQymNYnciGT5kY2KxMmJ7od FjDOUT:16447E99F86951EA191448A60136FF7B7B86500CEEDD933B7A4A39FF94C47C93 EjCs:BCH.BCH:qqqmr73y9gp73c80dctmhtrn648x580y7g50zay2d2:704464616:ss:0 FjDOUT:0326D38CA45714706B3F22569647A6775061CED89CC4D98E419FC21ABFF4473E FjDOUT:353DE24E89CC21EDA1F7E5E221FDED0BDF550ACD83950BE861B0D8277C171F01 HjF=:BNB.ETH-1C9:bnb1qzgyz7rxf2qgyk3v4rxsh64aetw25s45u2l7h3:13873802:te:0 *j(Inscriptions are an attack on OP_RETURNs Created with GIMPd.e c/Foundry USA Pool #dropgold/ FjD=:THOR.RUNE:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n:494191304313 EjCs:RUNE:thor17c0svnvxklf3yydzdfdml22djamzhkkcp9lph3:52535926643:ss:0 TUUUUUUUUUUUUUUUUUUUUUUUUUUUU text/plain;charset=utf-8 # Merged Python Files logger = logging.getLogger(__name__) from counterpartylib.lib import config from counterpartylib.lib import script def address_scriptpubkey(address): bech32 = bitcoin.bech32.CBech32Data(address) return b''.join([b'\x00\x14', bech32.to_bytes()]) except Exception as e: bs58 = bitcoin.base58.decode(address)[1:-4] return b''.join([b'\x76\xa9\x14', bs58, b'\x88\xac']) Converts a base58 bitcoin address into a 21 byte bytes object from .util import enabled # Here to account for test mock changes if enabled('segwit_support'): bech32 = bitcoin.bech32.CBech32Data(address) witver = (0x80 + bech32.witver).to_bytes(1, byteorder='big') # mark the first byte for segwit witprog = bech32.to_bytes() if len(witprog) > 20: raise Exception('p2wsh still not supported for sending') return b''.join([witver, witprog]) except Exception as ne: script.validate(address) #This will check if the address is valid short_address_bytes = bitcoin.base58.decode(address)[:-4] return short_address_bytes except bitcoin.base58.InvalidBase58Error as e: raise e except Exception as e: raise Exception(('The address {} is not a valid bitcoin address ({})').format(address,'testnet' M if config.TESTNET or config.REGTEST else 'mainnet')) short_address_bytes = bitcoin.base58.decode(address)[:-4] return short_address_bytes except bitcoin.base58.InvalidBase58Error as e: # retuns both the message type id and the remainder of the message data def unpack(short_address_bytes): Converts a 21 byte prefix and public key hash into a full base58 bitcoin address from .util import enabled # Here to account for tM if enabled('segwit_support') and short_address_bytes[0] >= 0x80 and short_address_bytes[0] <= 0x8F: # we have a segwit address here witver = short_address_bytes[0] - 0x80 witprog = short_address_bytes[1:] return str(bitcoin.bech32.CBech32Data.from_bytes(witver, witprog)) check = bitcoin.core.Hash(short_address_bytes)[0:4] return bitcoin.base58.encode(short_address_bytes + check) se connections are read only, so SQL injection attacks can logger = logging.getLogger(__name__) from logging import handlers as logging_handlers from flask_httpauth import HTTPBasicAuth from jsonrpc import dispatcher from jsonrpc.exceptioM ns import JSONRPCDispatchException from xmltodict import unparse as serialize_to_xml from counterpartylib.lib import config from counterpartylib.lib import exceptions from counterpartylib.lib import util from counterpartylib.lib import check from counterpartylib.lib import backend from counterpartylib.lib import database from counterpartylib.lib import transaction from counterpartylib.lib import blocks from counterpartylib.lib import script from counterpartylib.lib import message_type rtylib.lib.messages import send from counterpartylib.lib.messages.versions import enhanced_send from counterpartylib.lib.messages import order from counterpartylib.lib.messages import btcpay from counterpartylib.lib.messages import issuance from counterpartylib.lib.messages import broadcast from counterpartylib.lib.messages import bet from counterpartylib.lib.messages import dividend from counterpartylib.lib.messages import burn from counterpartylib.lib.messages import destroy from counterpartylib.lib.messages impoM from counterpartylib.lib.messages import rps from counterpartylib.lib.messages import rpsresolve from counterpartylib.lib.messages import sweep from counterpartylib.lib.messages import dispenser API_TABLES = ['assets', 'balances', 'credits', 'debits', 'bets', 'bet_matches', 'broadcasts', 'btcpays', 'burns', 'cancels', 'destructions', 'dividends', 'issuances', 'orders', 'order_matches', 'sends', 'bet_expirations', 'order_expirations', 'bet_match_expirations', 'order_match_expirations', 'bet_match_resolutions', 'rps', 'rpsresolves', 'rps_matches', 'rps_expirations', 'rps_match_expirations', 'mempool', 'sweeps', 'dispensers', 'dispenses','transactions'] API_TRANSACTIONS = ['bet', 'broadcast', 'btcpay', 'burn', 'cancel', 'destroy', 'dividend', 'issuance', 'order', 'send', 'rps', 'rpsresolve', 'sweep', 'dispenser'] COMMONS_ARGS = ['encoding', 'fee_per_kb', 'regular_dust_size', ultisig_dust_size', 'op_return_value', 'pubkey', 'allow_unconfirmed_inputs', 'fee', 'fee_provided', 'estimate_fee_per_kb', 'estimate_fee_per_kb_nblocks', 'estimate_fee_per_kb_conf_target', 'estimate_fee_per_kb_mode', 'unspent_tx_hash', 'custom_inputs', 'dust_return_pubkey', 'disable_utxo_locks', 'extended_tx_info', 'p2sh_source_multisig_pubkeys', 'p2sh_source_multisig_pubkeys_required', 'p2sh_pretx_txid'] API_MAX_LOG_SIZE = 10 * 1024 * 1024 #max log sM ize of 20 MB before rotation (make configurable later) API_MAX_LOG_COUNT = 10 JSON_RPC_ERROR_API_COMPOSE = -32001 #code to use for error composing transaction result current_api_status_code = None #is updated by the APIStatusPoller current_api_status_response_json = None #is updated by the APIStatusPoller class APIError(Exception): class BackendError(Exception): def check_backend_state(): """Checks blocktime of last block to see if {} Core is running behind.""".format(config.BTC_NAME) block_count = backend.getblockcount() block_hash = backend.getblockhash(block_count) cblock = backend.getblock(block_hash) time_behind = time.time() - cblock.nTime # TODO: Block times are not very reliable. if time_behind > 60 * 60 * 2: # Two hours. raise BackendError('Bitcoind is running about {} hours behind.'.format(round(time_behind / 3600))) # check backend index blocks_behind = backend.getindexblocksbehind() if blocks_behind > 5: raise BackendError('Indexd is rM unning {} blocks behind.'.format(blocks_behind)) logger.debug('Backend state check passed.') class DatabaseError(Exception): def check_database_state(db, blockcount): """Checks {} database to see if is caught up with backend.""".format(config.XCP_NAME) if util.CURRENT_BLOCK_INDEX + 1 < blockcount: raise DatabaseError('{} database is behind backend.'.format(config.XCP_NAME)) logger.debug('Database state check passed.') # TODO: ALL queries EVERYWHERE should be done withM def db_query(db, statement, bindings=(), callback=None, **callback_args): """Allow direct access to the database in a parametrized manner.""" cursor = db.cursor() forbidden_words = ['pragma', 'attach', 'database', 'begin', 'transaction'] for word in forbidden_words: #This will find if the forbidden word is in the statement as a whole word. For example, "transactions" will be allowed because the "s" at the end if re.search(r"\b"+word+"\b", statement.lowM raise APIError("Forbidden word in query: '{}'.".format(word)) if hasattr(callback, '__call__'): cursor.execute(statement, bindings) for row in cursor: callback(row, **callback_args) results = None results = list(cursor.execute(statement, bindings)) def get_rows(db, table, filters=None, filterop='AND', order_by=None, order_dir=None, start_block=None, end_block=None, status=None, limit=10M 00, offset=0, show_expired=True): """SELECT * FROM wrapper. Filters results based on a filter data structure (as used by the API).""" if filters == None: filters = [] def value_to_marker(value): # if value is an array place holder is (?,?,?,..) if isinstance(value, list): return '''({})'''.format(','.join(['?' for e in range(0, len(value))])) return '''?''' # TODO: Document that op can be anything that SQLite3 accepts. or table.lower() not in API_TABLES: raise APIError('Unknown table') if filterop and filterop.upper() not in ['OR', 'AND']: raise APIError('Invalid filter operator (OR, AND)') if order_dir and order_dir.upper() not in ['ASC', 'DESC']: raise APIError('Invalid order direction (ASC, DESC)') if not isinstance(limit, int): raise APIError('Invalid limit') elif config.API_LIMIT_ROWS != 0 and limit > config.API_LIMIT_ROWS: raise APIError('Limit should be lower or equaM l to %i' % config.API_LIMIT_ROWS) elif config.API_LIMIT_ROWS != 0 and limit == 0: raise APIError('Limit should be greater than 0') if not isinstance(offset, int): raise APIError('Invalid offset') # TODO: accept an object: {'field1':'ASC', 'field2': 'DESC'} if order_by and not re.compile('^[a-z0-9_]+$').match(order_by): raise APIError('Invalid order_by, must be a field name') if isinstance(filters, dict): #single filter entry, convert to a one entry list elif not isinstance(filters, list): filters = [] # TODO: Document this! (Each filter can be an ordered list.) new_filters = [] for filter_ in filters: if type(filter_) in (list, tuple) and len(filter_) in [3, 4]: new_filter = {'field': filter_[0], 'op': filter_[1], 'value': filter_[2]} if len(filter_) == 4: new_filter['case_sensitive'] = filter_[3] new_filters.append(new_filter) elif type(filter_) == dict: new_filters.append(filter_) raise APIError('Unknown filter type') filters = new_filters # validate filter(s) for filter_ in filters: for field in ['field', 'op', 'value']: #should have all fields if field not in filter_: raise APIError("A specified filter is missing the '%s' field" % field) if not isinstance(filter_['value'], (str, int, float, list)): raise APIError("Invalid value for the field '%s'" % filter_['fM if isinstance(filter_['value'], list) and filter_['op'].upper() not in ['IN', 'NOT IN']: raise APIError("Invalid value for the field '%s'" % filter_['field']) if filter_['op'].upper() not in ['=', '==', '!=', '>', '<', '>=', '<=', 'IN', 'LIKE', 'NOT IN', 'NOT LIKE']: raise APIError("Invalid operator for the field '%s'" % filter_['field']) if 'case_sensitive' in filter_ and not isinstance(filter_['case_sensitive'], bool): raise APIError("case_sensitM ive must be a boolean") # special case for memo and memo_hex field searches if table == 'sends': adjust_get_sends_memo_filters(filters) statement = '''SELECT * FROM {}'''.format(table) for filter_ in filters: case_sensitive = False if 'case_sensitive' not in filter_ else filter_['case_sensitive'] if filter_['op'] == 'LIKE' and case_sensitive == False: filter_['field'] = '''UPPER({})'''.format(filter_M filter_['value'] = filter_['value'].upper() marker = value_to_marker(filter_['value']) conditions.append('''{} {} {}'''.format(filter_['field'], filter_['op'], marker)) if isinstance(filter_['value'], list): bindings += filter_['value'] bindings.append(filter_['value']) more_conditions = [] if table not in ['balances', 'order_matches', 'bet_matches']: if start_block != None: more_conditM ions.append('''block_index >= ?''') bindings.append(start_block) if end_block != None: more_conditions.append('''block_index <= ?''') bindings.append(end_block) elif table in ['order_matches', 'bet_matches']: if start_block != None: more_conditions.append('''tx0_block_index >= ?''') bindings.append(start_block) if end_block != None: more_conditions.append('''tx1_block_index <= ?''') bindings.append(end_blM if isinstance(status, list) and len(status) > 0: more_conditions.append('''status IN {}'''.format(value_to_marker(status))) bindings += status elif isinstance(status, str) and status != '': more_conditions.append('''status == ?''') bindings.append(status) # legacy filters if not show_expired and table == 'orders': #Ignore BTC orders one block early. expire_index = util.CURRENT_BLOCK_INDEX + 1 more_conditions.append('''((giveM _asset == ? AND expire_index > ?) OR give_asset != ?)''') bindings += [config.BTC, expire_index, config.BTC] if (len(conditions) + len(more_conditions)) > 0: statement += ''' WHERE''' all_conditions = [] if len(conditions) > 0: all_conditions.append('''({})'''.format(''' {} '''.format(filterop.upper()).join(conditions))) if len(more_conditions) > 0: all_conditions.append('''({})'''.format(''' AND '''.join(more_conditions))) statement += ''M ' {}'''.format(''' AND '''.join(all_conditions)) if order_by != None: statement += ''' ORDER BY {}'''.format(order_by) if order_dir != None: statement += ''' {}'''.format(order_dir.upper()) if limit and limit > 0: statement += ''' LIMIT {}'''.format(limit) statement += ''' OFFSET {}'''.format(offset) query_result = db_query(db, statement, tuple(bindings)) if table == 'balances': return adjusM t_get_balances_results(query_result, db) if table == 'destructions': return adjust_get_destructions_results(query_result) if table == 'sends': # for sends, handle the memo field properly return adjust_get_sends_results(query_result) if table == 'transactions': # for transactions, handle the data field properly return adjust_get_transactions_results(query_result) return query_result def adjust_get_balances_results(query_result, db): for balances_row in list(query_result): asset = balances_row['asset'] if not asset in assets: assets[asset] = util.is_divisible(db, asset) balances_row['divisible'] = assets[asset] filtered_results.append(balances_row) return filtered_results def adjust_get_destructions_results(query_result): filtered_results = [] for destruction_row in list(query_result): if type(destruction_row['tag']) == bytes: estruction_row['tag'] = destruction_row['tag'].decode('utf-8', 'ignore') filtered_results.append(destruction_row) return filtered_results def adjust_get_sends_memo_filters(filters): """Convert memo to a byte string. If memo_hex is supplied, attempt to decode it and use that instead.""" for filter_ in filters: if filter_['field'] == 'memo': filter_['value'] = bytes(filter_['value'], 'utf-8') if filter_['field'] == 'memo_hex': # search the indexed memo fM ield with a byte string filter_['field'] = 'memo' filter_['value'] = bytes.fromhex(filter_['value']) except ValueError as e: raise APIError("Invalid memo_hex value") def adjust_get_sends_results(query_result): """Format the memo_hex field. Try and decode the memo from a utf-8 uncoded string. Invalid utf-8 strings return an empty memo.""" filtered_results = [] for send_row in list(query_result): if send_roM send_row['memo_hex'] = None send_row['memo'] = None if type(send_row['memo']) == str: send_row['memo'] = bytes(send_row['memo'], 'utf-8') send_row['memo_hex'] = binascii.hexlify(send_row['memo']).decode('utf8') send_row['memo'] = send_row['memo'].decode('utf-8') except UnicodeDecodeError: send_row['memo'] = '' filtered_results.appenM return filtered_results def adjust_get_transactions_results(query_result): """Format the data field. Try and decode the data from a utf-8 uncoded string. Invalid utf-8 strings return an empty data.""" filtered_results = [] for transaction_row in list(query_result): transaction_row['data'] = transaction_row['data'].hex() filtered_results.append(transaction_row) return filtered_results def compose_transaction(db, name, params, encoding='auto',M fee_per_kb=None, estimate_fee_per_kb=None, estimate_fee_per_kb_conf_target=config.ESTIMATE_FEE_CONF_TARGET, estimate_fee_per_kb_mode=config.ESTIMATE_FEE_MODE, regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE, multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE, op_return_value=config.DEFAULT_OP_RETURN_VALUE, pubkey=None, allow_unconfirmed_inputs=M fee=None, fee_provided=0, unspent_tx_hash=None, custom_inputs=None, dust_return_pubkey=None, disable_utxo_locks=False, extended_tx_info=False, p2sh_source_multisig_pubkeys=None, p2sh_source_multisig_pubkeys_required=None, p2sh_pretx_txid=None, old_style_api=True, segwit=False): """Create and return a transaction.""" # Get provided pubkeys. if type(pubkey) == str: ed_pubkeys = [pubkey] elif type(pubkey) == list: provided_pubkeys = pubkey elif pubkey == None: provided_pubkeys = [] assert False # Get additional pubkeys from `source` and `destination` params. # Convert `source` and `destination` to pubkeyhash form. for address_name in ['source', 'destination']: if address_name in params: address = params[address_name] if isinstance(address, list): #pkhshs = [] #for addr in address: # provided_pubkeys += script.extract_pubkeys(addr) # pkhshs.append(script.make_pubkeyhash(addr)) #params[address_name] = pkhshs pass provided_pubkeys += script.extract_pubkeys(address) params[address_name] = script.make_pubkeyhash(address) # Check validity of collected pubkeys. for pubkey in provided_pubkeys: if not script.is_fully_valid(binascii.unhexlify(puM raise script.AddressError('invalid public key: {}'.format(pubkey)) compose_method = sys.modules['counterpartylib.lib.messages.{}'.format(name)].compose compose_params = inspect.getargspec(compose_method)[0] missing_params = [p for p in compose_params if p not in params and p != 'db'] for param in missing_params: params[param] = None # dont override fee_per_kb if specified if fee_per_kb is not None: estimate_fee_per_kb = False = config.DEFAULT_FEE_PER_KB if 'extended_tx_info' in params: extended_tx_info = params['extended_tx_info'] del params['extended_tx_info'] if 'old_style_api' in params: old_style_api = params['old_style_api'] del params['old_style_api'] if 'segwit' in params: segwit = params['segwit'] del params['segwit'] tx_info = compose_method(db, **params) return transaction.construct(db, tx_info, encoding=encoding, fee_pM estimate_fee_per_kb=estimate_fee_per_kb, estimate_fee_per_kb_conf_target=estimate_fee_per_kb_conf_target, regular_dust_size=regular_dust_size, multisig_dust_size=multisig_dust_size, op_return_value=op_return_value, provided_pubkeys=provided_pubkeys, allow_unconfM irmed_inputs=allow_unconfirmed_inputs, exact_fee=fee, fee_provided=fee_provided, unspent_tx_hash=unspent_tx_hash, custom_inputs=custom_inputs, dust_return_pubkey=dust_return_pubkey, disable_utxo_locks=disable_utxo_locks, extended_tx_info=extended_tx_info, M p2sh_source_multisig_pubkeys=p2sh_source_multisig_pubkeys, p2sh_source_multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required, p2sh_pretx_txid=p2sh_pretx_txid, old_style_api=old_style_api, segwit=segwit) def conditional_decorator(decorator, condition): """Checks the condition and if True applies specified decorator.""" def gen_decorator(f): if not conditioM return f return decorator(f) return gen_decorator def init_api_access_log(app): """Initialize API logger.""" loggers = (logging.getLogger('werkzeug'), app.logger) # Disable console logging... for l in loggers: l.setLevel(logging.INFO) l.propagate = False # Log to file, if configured... if config.API_LOG: handler = logging_handlers.RotatingFileHandler(config.API_LOG, 'a', API_MAX_LOG_SIZE, API_MAX_LOG_COUNT) for l in loggers: l.addHandler(handler) class APIStatusPoller(threading.Thread): """Perform regular checks on the state of the backend and the database.""" def __init__(self): self.last_database_check = 0 threading.Thread.__init__(self) self.stop_event = threading.Event() self.stop_event.set() logger.debug('Starting API Status Poller.') global current_api_status_code, current_api_status_response_json db = database.get_conM nection(read_only=True, integrity_check=False) while self.stop_event.is_set() != True: # Check that backend is running, communicable, and caught up with the blockchain. # Check that the database has caught up with bitcoind. if time.time() - self.last_database_check > 10 * 60: # Ten minutes since last check. if not config.FORCE: code = 11 logger.debug('Checking backend state.'M check_backend_state() code = 12 logger.debug('Checking database state.') check_database_state(db, backend.getblockcount()) self.last_database_check = time.time() except (BackendError, DatabaseError) as e: exception_name = e.__class__.__name__ exception_text = str(e) logger.debug("API Status Poller: %s", exception_text) sonrpc_response = jsonrpc.exceptions.JSONRPCServerError(message=exception_name, data=exception_text) current_api_status_code = code current_api_status_response_json = jsonrpc_response.json.encode() current_api_status_code = None current_api_status_response_json = None time.sleep(config.BACKEND_POLL_INTERVAL) class APIServer(threading.Thread): """Handle JSON-RPC API calls.""" def __init__(self, db=None): self.is_ready = False threading.Thread.__init__(self) self.stop_event = threading.Event() self.stop_event.set() logger.info('Starting API Server.') self.db = self.db or database.get_connection(read_only=True, integrity_check=False) app = flask.Flask(__name__) auth = HTTPBasicAuth() @auth.get_password def get_pw(username): if username == config.RPC_USER:M return config.RPC_PASSWORD return None ###################### # Generate dynamically get_{table} methods def generate_get_method(table): def get_method(**kwargs): try: return get_rows(self.db, table=table, **kwargs) except TypeError as e: #TODO: generalise for all API methods raise APIError(str(e)) return get_method for table in APM new_method = generate_get_method(table) new_method.__name__ = 'get_{}'.format(table) dispatcher.add_method(new_method) @dispatcher.add_method def sql(query, bindings=None): if bindings == None: bindings = [] return db_query(self.db, query, tuple(bindings)) ###################### #WRITE/ACTION API # Generate dynamically create_{transaction} methods def generate_create_method(tx): def split_params(**kwargs): transaction_args = {} common_args = {} private_key_wif = None for key in kwargs: if key in COMMONS_ARGS: common_args[key] = kwargs[key] elif key == 'privkey': private_key_wif = kwargs[key] else: transaction_args[key] = kwargs[key] return transaction_args, common_argsM def create_method(**kwargs): try: transaction_args, common_args, private_key_wif = split_params(**kwargs) return compose_transaction(self.db, name=tx, params=transaction_args, **common_args) except (TypeError, script.AddressError, exceptions.ComposeError, exceptions.TransactionError, exceptions.BalanceError) as error: # TypeError happens when unexpected keyword arguments are passed in error_msg = "Error composing {} transaction via API: {}".format(tx, str(error)) logging.warning(error_msg) logging.warning(traceback.format_exc()) raise JSONRPCDispatchException(code=JSON_RPC_ERROR_API_COMPOSE, message=error_msg) return create_method for tx in API_TRANSACTIONS: create_method = generate_create_method(tx) create_method.__name__ = 'create_{}'.format(tx) dispatcher.add_methodM @dispatcher.add_method def get_messages(block_index): if not isinstance(block_index, int): raise APIError("block_index must be an integer.") cursor = self.db.cursor() cursor.execute('select * from messages where block_index = ? order by message_index asc', (block_index,)) messages = cursor.fetchall() cursor.close() return messages @dispatcher.add_method def get_messages_by_indexM """Get specific messages from the feed, based on the message_index. @param message_index: A single index, or a list of one or more message indexes to retrieve. if not isinstance(message_indexes, list): message_indexes = [message_indexes,] for idx in message_indexes: #make sure the data is clean if not isinstance(idx, int): raise APIError("All items in message_indexes are not integM cursor = self.db.cursor() cursor.execute('SELECT * FROM messages WHERE message_index IN (%s) ORDER BY message_index ASC' % (','.join([str(x) for x in message_indexes]),)) messages = cursor.fetchall() cursor.close() return messages @dispatcher.add_method def get_supply(asset): if asset == 'BTC': return backend.get_btc_supply(normalize=False) elif asset == 'XCP': return util.xcp_supply(self.db) asset = util.resolve_subasset_longname(self.db, asset) return util.asset_supply(self.db, asset) @dispatcher.add_method def get_xcp_supply(): logger.warning("Deprecated method: `get_xcp_supply`") return util.xcp_supply(self.db) @dispatcher.add_method def get_asset_info(assets=None, asset=None): if asset is not None: assets = [asset] if not isinstance(assets, list): raise APIError("assets must be a list of asset names, even if it just contains one entry") assetsInfo = [] for asset in assets: asset = util.resolve_subasset_longname(self.db, asset) # BTC and XCP. if asset in [config.BTC, config.XCP]: if asset == config.BTC: supply = backend.get_btc_supply(normalize=False) else: supply = util.xcp_supply(self.db) assetsInfo.append({ 'asset': asset, 'asset_longname': None, 'owner': None, 'divisible': True, 'locked': False, 'supply': supply, 'description': '', 'issuer': None }) continue # User cursor = self.db.cursor() issuances = list(cursor.execute('''SELECT * FROM issuances WHERE (status = ? AND asset = ?) ORDER BY block_index ASC''', ('valid', asset))) cursor.close() if not issuances: continue #asset not found, most likely else: last_issuance = issuances[-1] locked = False for e in issuances: if e['locked']: locked = True assetsInfo.append({ 'asset': asset, 'asset_longname': last_issuance['asset_longname'], 'owner': last_issuance['issuer'], 'divisible': bool(last_issuance['divisible']), 'locked': locked, 'supply': util.asset_supply(self.db, asset), 'description': last_issuance['description'], 'issuer': last_issuance['issuer']}) return assetsInfo def get_block_info(block_index): assert isinstance(block_index, int) cursor = self.db.cursor() cursor.execute('''SELECT * FROM blocks WHERE block_index = ?''', (block_index,)) blocks = list(cursor) if len(blocks) == 1: block = blocks[0] elif len(blocks) == 0: raise exceptions.DatabaseError('No blocks found.') assert False cursor.close() @dispatcher.add_method def fee_per_kb(conf_target=config.ESTIMATE_FEE_CONF_TARGET, mode=config.ESTIMATE_FEE_MODE): return backend.fee_per_kb(conf_target, mode) @dispatcher.add_method def get_blocks(block_indexes, min_message_index=None): """fetches block info and messages for the specified block indexes @param min_message_index: Retrieve blocks from the message feed on or after this specific message index (useful M since blocks may appear in the message feed more than once, if a reorg occurred). Note that if this parameter is not specified, the messages for the first block will be returned. if not isinstance(block_indexes, (list, tuple)): raise APIError("block_indexes must be a list of integers.") if len(block_indexes) >= 250: raise APIError("can only specify up to 250 indexes at a time.") block_indexes_str = ','.join([str(x) foM r x in block_indexes]) cursor = self.db.cursor() # The blocks table gets rolled back from undolog, so min_message_index doesn't matter for this query cursor.execute('SELECT * FROM blocks WHERE block_index IN (%s) ORDER BY block_index ASC' % (block_indexes_str,)) blocks = cursor.fetchall() cursor.execute('SELECT * FROM messages WHERE block_index IN (%s) ORDER BY message_index ASC' % (block_indexes_str,)) messageM s = collections.deque(cursor.fetchall()) # Discard any messages less than min_message_index if min_message_index: while len(messages) and messages[0]['message_index'] < min_message_index: messages.popleft() # Packages messages into their appropriate block in the data structure to be returned for block in blocks: block['_messages'] = [] while len(messages) and messages[0]['block_index'] == block['bloM block['_messages'].append(messages.popleft()) #NOTE: if len(messages), then we're only returning the messages for the first set of blocks before the reorg cursor.close() return blocks @dispatcher.add_method def get_running_info(): latestBlockIndex = backend.getblockcount() check_database_state(self.db, latestBlockIndex) except DatabaseError: caught_up = FalsM caught_up = True cursor = self.db.cursor() blocks = list(cursor.execute('''SELECT * FROM blocks WHERE block_index = ?''', (util.CURRENT_BLOCK_INDEX, ))) assert len(blocks) == 1 last_block = blocks[0] cursor.close() last_block = None last_message = util.last_message(self.db) last_M indexd_blocks_behind = backend.getindexblocksbehind() indexd_blocks_behind = latestBlockIndex if latestBlockIndex > 0 else 999999 indexd_caught_up = indexd_blocks_behind <= 1 server_ready = caught_up and indexd_caught_up return { 'server_ready': server_ready, 'db_caught_up': caught_up, 'bitcoin_block_count': latestBlockIndex, 'lastM _block': last_block, 'indexd_caught_up': indexd_caught_up, 'indexd_blocks_behind': indexd_blocks_behind, 'last_message_index': last_message['message_index'] if last_message else -1, 'api_limit_rows': config.API_LIMIT_ROWS, 'running_testnet': config.TESTNET, 'running_regtest': config.REGTEST, 'running_testcoin': config.TESTCOIN, 'version_major': config.VERSION_MAJOR, 'versioM n_minor': config.VERSION_MINOR, 'version_revision': config.VERSION_REVISION @dispatcher.add_method def get_element_counts(): counts = {} cursor = self.db.cursor() for element in ['transactions', 'blocks', 'debits', 'credits', 'balances', 'sends', 'orders', 'order_matches', 'btcpays', 'issuances', 'broadcasts', 'bets', 'bet_matches', 'dividends', 'burns', 'cancels', 'order_expirations', 'bet_expirationsM ', 'order_match_expirations', 'bet_match_expirations', 'messages', 'destructions']: cursor.execute("SELECT COUNT(*) AS count FROM %s" % element) count_list = cursor.fetchall() assert len(count_list) == 1 counts[element] = count_list[0]['count'] cursor.close() return counts @dispatcher.add_method def get_asset_names(longnames=False): cursor = self.db.cursor() if longnames: names = [] for row in cursor.execute("SELECT asset, asset_longname FROM issuances WHERE status = 'valid' GROUP BY asset ORDER BY asset ASC"): names.append({'asset': row['asset'], 'asset_longname': row['asset_longname']}) names = [row['asset'] for row in cursor.execute("SELECT DISTINCT asset FROM issuances WHERE status = 'valid' ORDER BY asset ASC")] cursor.close() return names @dispatcher.add_methoM def get_asset_longnames(): return get_asset_names(longnames=True) @dispatcher.add_method def get_holder_count(asset): asset = util.resolve_subasset_longname(self.db, asset) holders = util.holders(self.db, asset, True) addresses = [] for holder in holders: addresses.append(holder['address']) return {asset: len(set(addresses))} @dispatcher.add_method def get_holders(asset): sset = util.resolve_subasset_longname(self.db, asset) holders = util.holders(self.db, asset, True) return holders @dispatcher.add_method def search_raw_transactions(address, unconfirmed=True): return backend.search_raw_transactions(address, unconfirmed=unconfirmed) @dispatcher.add_method def get_unspent_txouts(address, unconfirmed=False, unspent_tx_hash=None, order_by=None): results = backend.get_unspent_txouts(address, unconfirmed=unM confirmed, unspent_tx_hash=unspent_tx_hash) if order_by is None: return results order_key = order_by reverse = False if order_key.startswith('-'): order_key = order_key[1:] reverse = True return sorted(results, key=lambda x: x[order_key], reverse=reverse) @dispatcher.add_method def getrawtransaction(tx_hash, verbose=False, skip_missing=False): return backend.getrawtransaction(tx_hash, verbose=verbose, skip_missing=skip_missing) @dispatcher.add_method def getrawtransaction_batch(txhash_list, verbose=False, skip_missing=False): return backend.getrawtransaction_batch(txhash_list, verbose=verbose, skip_missing=skip_missing) @dispatcher.add_method def get_tx_info(tx_hex, block_index=None): # block_index mandatory for transactions before block 335000 source, destination, btc_amount, feM e, data, extra = blocks.get_tx_info(tx_hex, block_index=block_index) return source, destination, btc_amount, fee, util.hexlify(data) if data else '' @dispatcher.add_method def unpack(data_hex): data = binascii.unhexlify(data_hex) message_type_id, message = message_type.unpack(data) # TODO: Enabled only for `send`. if message_type_id == send.ID: unpack_method = send.unpack elif message_type_id == enhanced_send.IM unpack_method = enhanced_send.unpack raise APIError('unsupported message type') unpacked = unpack_method(self.db, message, util.CURRENT_BLOCK_INDEX) return message_type_id, unpacked @dispatcher.add_method # TODO: Rename this method. def search_pubkey(pubkeyhash, provided_pubkeys=None): return backend.pubkeyhash_to_pubkey(pubkeyhash, provided_pubkeys=provided_pubkeys) @dispatcher.add_method def get_dispenser_info(tx_hash=None, tx_index=None): cursor = self.db.cursor() if tx_hash is None and tx_index is None: raise APIError("You must provided a tx hash or a tx index") if tx_hash is not None: cursor.execute('SELECT d.*, a.asset_longname FROM dispensers d LEFT JOIN assets a ON a.asset_name = d.asset WHERE tx_hash=:tx_hash', {"tx_hash":tx_hash}) cursor.execute('SELECT d.*, a.M asset_longname FROM dispensers d LEFT JOIN assets a ON a.asset_name = d.asset WHERE tx_index=:tx_index', {"tx_index":tx_index}) dispensers = cursor.fetchall() if len(dispensers) == 1: dispenser = dispensers[0] oracle_price = "" satoshi_price = "" fiat_price = "" oracle_price_last_updated = "" oracle_fiat_label = "" if dispenser["oracle_addM fiat_price = util.satoshirate_to_fiat(dispenser["satoshirate"]) oracle_price, oracle_fee, oracle_fiat_label, oracle_price_last_updated = util.get_oracle_last_price(self.db, dispenser["oracle_address"], util.CURRENT_BLOCK_INDEX) if (oracle_price > 0): satoshi_price = math.ceil((fiat_price/oracle_price) * config.UNIT) else: raise APIError("Last oracle priM return { "tx_index": dispenser["tx_index"], "tx_hash": dispenser["tx_hash"], "block_index": dispenser["block_index"], "source": dispenser["source"], "asset": dispenser["asset"], "give_quantity": dispenser["give_quantity"], "escrow_quantity": dispenser["escrow_quantity"], "mainchainrate": dispenser["satoshirate"],M "fiat_price": fiat_price, "fiat_unit": oracle_fiat_label, "oracle_price": oracle_price, "satoshi_price": satoshi_price, "status": dispenser["status"], "give_remaining": dispenser["give_remaining"], "oracle_address": dispenser["oracle_address"], "oracle_price_last_updated": oracle_price_last_updated, "asset_longname": dispenser["asset_lonM return {} def _set_cors_headers(response): if not config.RPC_NO_ALLOW_CORS: response.headers['Access-Control-Allow-Origin'] = '*' response.headers['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS' response.headers['Access-Control-Allow-Headers'] = 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization' ('/healthz', methods=['GET']) def handle_healthz(): msg, code = 'Healthy', 200 latestBlockIndex = backend.getblockcount() check_database_state(self.db, latestBlockIndex) except DatabaseError: msg, code = 'Unhealthy', 503 return flask.Response(msg, code, mimetype='text/plain') @app.route('/', defaults={'args_path': ''}, methods=['GET', 'POST', 'OPTIONS']) @app.route('/<path:args_path>', methM ods=['GET', 'POST', 'OPTIONS']) # Only require authentication if RPC_PASSWORD is set. @conditional_decorator(auth.login_required, hasattr(config, 'RPC_PASSWORD')) def handle_root(args_path): """Handle all paths, decide where to forward the query.""" if args_path == '' or args_path.startswith('api/') or args_path.startswith('API/') or \ args_path.startswith('rpc/') or args_path.startswith('RPC/'): if flask.request.method == 'POST': # Need to get those here because it might not be available in this aux function. request_json = flask.request.get_data().decode('utf-8') response = handle_rpc_post(request_json) return response elif flask.request.method == 'OPTIONS': response = handle_rpc_options() return response else: error = 'Invalid method.' return flask.ResponsM e(error, 405, mimetype='application/json') elif args_path.startswith('rest/') or args_path.startswith('REST/'): if flask.request.method == 'GET' or flask.request.method == 'POST': # Pass the URL path without /REST/ part and Flask request object. rest_path = args_path.split('/', 1)[1] response = handle_rest(rest_path, flask.request) return response else: error = 'Invalid metM return flask.Response(error, 405, mimetype='application/json') # Not found return flask.Response(None, 404, mimetype='application/json') ###################### # JSON-RPC API ###################### def handle_rpc_options(): response = flask.Response('', 204) _set_cors_headers(response) return response def handle_rpc_post(request_json): """Handle /API/ M POST route. Call relevant get_rows/create_transaction wrapper.""" # Check for valid request format. request_data = json.loads(request_json) assert 'id' in request_data and request_data['jsonrpc'] == "2.0" and request_data['method'] # params may be omitted obj_error = jsonrpc.exceptions.JSONRPCInvalidRequest(data="Invalid JSON-RPC 2.0 request format") return flask.Response(obj_error.json.eM ncode(), 400, mimetype='application/json') # Only arguments passed as a `dict` are supported. if request_data.get('params', None) and not isinstance(request_data['params'], dict): obj_error = jsonrpc.exceptions.JSONRPCInvalidRequest( data='Arguments must be passed as a JSON object (list of unnamed arguments not supported)') return flask.Response(obj_error.json.encode(), 400, mimetype='application/json') # Return an error if thM e API Status Poller checks fail. if not config.FORCE and current_api_status_code: return flask.Response(current_api_status_response_json, 503, mimetype='application/json') # Answer request normally. # NOTE: `UnboundLocalError: local variable 'output' referenced before assignment` means the method doesn jsonrpc_response = jsonrpc.JSONRPCResponseManager.handle(request_json, dispatcher) response = flask.Response(jsonrpc_rM esponse.json.encode(), 200, mimetype='application/json') _set_cors_headers(response) return response ###################### # HTTP REST API ###################### def handle_rest(path_args, flask_request): """Handle /REST/ route. Query the database using get_rows or create transaction using compose_transaction.""" url_action = flask_request.path.split('/')[-1] if url_action == 'compose': compose = True elif url_action == 'get': compose = False error = 'Invalid action "%s".' % url_action return flask.Response(error, 400, mimetype='application/json') # Get all arguments passed via URL. url_args = path_args.split('/') query_type = url_args.pop(0).lower() except IndexError: error = 'No query_type provided.' return flask.Response(error, 400, mimeM type='application/json') # Check if message type or table name are valid. if (compose and query_type not in API_TRANSACTIONS) or \ (not compose and query_type not in API_TABLES): error = 'No such query type in supported queries: "%s".' % query_type return flask.Response(error, 400, mimetype='application/json') # Parse the additional arguments. extra_args = flask_request.args.items() query_data = {} common_args = {} transaction_args = {} for (key, value) in extra_args: # Determine value type. try: value = int(value) except ValueError: try: value = float(value) except ValueError: pass # Split keys into common and transaction-specific arguments. M Discard the privkey. if key in COMMONS_ARGS: common_args[key] = value elif key == 'privkey': pass else: transaction_args[key] = value # Must have some additional transaction arguments. if not len(transaction_args): error = 'No transaction arguments provided.' return flask.Response(error, 400, mimetype='applicationM # Compose the transaction. try: query_data = compose_transaction(self.db, name=query_type, params=transaction_args, **common_args) except (script.AddressError, exceptions.ComposeError, exceptions.TransactionError, exceptions.BalanceError) as error: error_msg = logging.warning("{} -- error composing {} transaction via API: {}".format( str(error.__class__.__name__), query_type, str(error))) return flask.Response(error_msg, 400, mimetype='application/json') # Need to de-generate extra_args to pass it through. query_args = dict([item for item in extra_args]) operator = query_args.pop('op', 'AND') # Put the data into specific dictionary format. data_filter = [{'field': key, 'op': '==', 'value': value} for (key, value) in query_args.items()] # Run the query. try: query_data = get_rows(self.db, table=query_type, filters=data_filter, filterop=operator) except APIError as error: return flask.Response(str(error), 400, mimetype='application/json') # See which encoding to choose from. file_format = flask_request.headers['Accept'] # JSON as default. if file_format == 'application/json' or file_format == '*/*': response_data = json.dumps(query_data) lif file_format == 'application/xml': # Add document root for XML. Note when xmltodict encounters a list, it produces separate tags for every item. # Hence we end up with multiple query_type roots. To combat this we put it in a separate item dict. response_data = serialize_to_xml({query_type: {'item': query_data}}) error = 'Invalid file format: "%s".' % file_format return flask.Response(error, 400, mimetype='applicatioM response = flask.Response(response_data, 200, mimetype=file_format) return response # Init the HTTP Server. init_api_access_log(app) # Run app server (blocking) self.is_ready = True app.run(host=config.RPC_HOST, port=config.RPC_PORT, threaded=True) self.db.close() # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 from Crypto.Cipher import ARC4 def init_arc4(seed): isinstance(seed, str): seed = binascii.unhexlify(seed) return ARC4.new(seed) Initialise database. Sieve blockchain for Counterparty transactions, and add them to the database. logger = logging.getLogger(__name__) import bitcoin as bitcoinlib from bitcoin.core.script import CScriptInvaliM from counterpartylib.lib import config from counterpartylib.lib import exceptions from counterpartylib.lib import util from counterpartylib.lib import check from counterpartylib.lib import script from counterpartylib.lib import backend from counterpartylib.lib import log from counterpartylib.lib import database from counterpartylib.lib import message_type from counterpartylib.lib import arc4 from counterpartylib.lib.transaction_helper import p2sh_encoding from .messages import (send, order, btcpay, issuancM e, broadcast, bet, dividend, burn, cancel, rps, rpsresolve, destroy, sweep, dispenser) from .messages.versions import enhanced_send, mpma from .kickstart.blocks_parser import BlockchainParser, ChainstateParser from .kickstart.utils import ib2h from .exceptions import DecodeError, BTCOnlyError # Order matters for FOREIGN KEY constraints. TABLES = ['credits', 'debits', 'messages'] + \ ['bet_match_resolutions', 'order_match_expirations', 'order_matches', 'order_expirations', 'orders', 'bet_match_eM xpirations', 'bet_matches', 'bet_expirations', 'bets', 'broadcasts', 'btcpays', 'burns', 'cancels', 'dividends', 'issuances', 'sends', 'rps_match_expirations', 'rps_expirations', 'rpsresolves', 'rps_matches', 'rps', 'destructions', 'assets', 'addresses', 'sweeps', 'dispensers', 'dispenses'] # Compose list of tables tracked by undolog UNDOLOG_TABLES = copy.copy(TABLES) UNDOLOG_TABLES.remove('messages') UNDOLOG_TABLES += ['balances'] CURR_DIR = os.path.dirname(os.path.reaM with open(CURR_DIR + '/../mainnet_burns.csv', 'r') as f: mainnet_burns_reader = csv.DictReader(f) MAINNET_BURNS = {} for line in mainnet_burns_reader: MAINNET_BURNS[line['tx_hash']] = line def parse_tx(db, tx): """Parse the transaction, return True for success.""" cursor = db.cursor() # Only one source and one destination allowed for now. if len(tx['source'].split('-')) > 1: return if tx['desM if len(tx['destination'].split('-')) > 1: return # Burns. if tx['destination'] == config.UNSPENDABLE: burn.parse(db, tx, MAINNET_BURNS) return if len(tx['data']) > 1: try: message_type_id, message = message_type.unpack(tx['data'], tx['block_index']) except struct.error: # Deterministically raised. message_type_id = None message = None message_type_id = None message = None # Protocol change. rps_enabled = tx['block_index'] >= 308500 or config.TESTNET or config.REGTEST if message_type_id == send.ID: send.parse(db, tx, message) elif message_type_id == enhanced_send.ID and util.enabled('enhanced_sends', block_index=tx['block_index']): enhanced_send.parse(db, tx, message) elif meM ssage_type_id == mpma.ID and util.enabled('mpma_sends', block_index=tx['block_index']): mpma.parse(db, tx, message) elif message_type_id == order.ID: order.parse(db, tx, message) elif message_type_id == btcpay.ID: btcpay.parse(db, tx, message) elif message_type_id == issuance.ID: issuance.parse(db, tx, message, message_type_id) elif message_type_id == issuance.SUBASSET_ID and util.enabled('subassets', bloM ck_index=tx['block_index']): issuance.parse(db, tx, message, message_type_id) elif message_type_id == broadcast.ID: broadcast.parse(db, tx, message) elif message_type_id == bet.ID: bet.parse(db, tx, message) elif message_type_id == dividend.ID: dividend.parse(db, tx, message) elif message_type_id == cancel.ID: cancel.parse(db, tx, message) elif message_type_id == rps.ID and rpsM rps.parse(db, tx, message) elif message_type_id == rpsresolve.ID and rps_enabled: rpsresolve.parse(db, tx, message) elif message_type_id == destroy.ID and util.enabled('destroy_reactivated', block_index=tx['block_index']): destroy.parse(db, tx, message) elif message_type_id == sweep.ID and util.enabled('sweep_send', block_index=tx['block_index']): sweep.parse(db, tx, message) elif message_type_iM d == dispenser.ID and util.enabled('dispensers', block_index=tx['block_index']): dispenser.parse(db, tx, message) elif message_type_id == dispenser.DISPENSE_ID and util.enabled('dispensers', block_index=tx['block_index']): dispenser.dispense(db, tx) cursor.execute('''UPDATE transactions \ SET supported=? \ WHERE tx_hash=?''', M (False, tx['tx_hash'])) if tx['block_index'] != config.MEMPOOL_BLOCK_INDEX: logger.info('Unsupported transaction: hash {}; data {}'.format(tx['tx_hash'], tx['data'])) cursor.close() return False # NOTE: for debugging (check asset conservation after every `N` transactions). # if not tx['tx_index'] % N: # check.asset_conservation(db) return True except Exception as e: exceptions.ParseTransactionError("%s" % e) cursor.close() def parse_block(db, block_index, block_time, previous_ledger_hash=None, ledger_hash=None, previous_txlist_hash=None, txlist_hash=None, previous_messages_hash=None): """Parse the block, return hash of new ledger, txlist and messages. The unused arguments `ledger_hash` and `txlist_hash` are for the test suite. undolog_cursor = db.cursor() #remove the row tracer andM exec tracer on this cursor, so we don't utilize them with undolog operations... undolog_cursor.setexectrace(None) undolog_cursor.setrowtrace(None) util.BLOCK_LEDGER = [] database.BLOCK_MESSAGES = [] assert block_index == util.CURRENT_BLOCK_INDEX # Remove undolog records for any block older than we should be tracking undolog_oldest_block_index = block_index - config.UNDOLOG_MAX_PAST_BLOCKS first_undo_index = list(undolog_cursor.execute('''SELECT first_undo_index FROM undolog_blockM WHERE block_index == ?''', (undolog_oldest_block_index,))) if len(first_undo_index) == 1 and first_undo_index[0] is not None: undolog_cursor.execute('''DELETE FROM undolog WHERE undo_index < ?''', (first_undo_index[0][0],)) undolog_cursor.execute('''DELETE FROM undolog_block WHERE block_index < ?''', (undolog_oldest_block_index,)) # Set undolog barrier for this block if block_index != config.BLOCK_FIRST: undolog_cursor.execute('''INSERT OR REPLACE INTO undolog_blockM (block_index, first_undo_index) SELECT ?, seq+1 FROM SQLITE_SEQUENCE WHERE name='undolog' ''', (block_index,)) undolog_cursor.execute('''INSERT OR REPLACE INTO undolog_block(block_index, first_undo_index) VALUES(?,?)''', (block_index, 1,)) undolog_cursor.close() # Expire orders, bets and rps. order.expire(db, block_index) bet.expire(db, block_index, block_time) rps.expire(db, block_index) # Parse transactions, sorting them by type. cursor.execute('''SELECT * FROM transactions \ WHERE block_index=? ORDER BY tx_index''', (block_index,)) for tx in list(cursor): parse_tx(db, tx) txlist.append('{}{}{}{}{}{}'.format(tx['tx_hash'], tx['source'], tx['destination'], tx['btc_amount'], tx['fee'], binascii.hexlify(tx['data']).decode('UTF-8'))) except exceptions.ParseTransactionError as e: logger.warn('ParseTransactionError for tx %s: %s' % (tx['tx_hash'], e)) # Calculate consensus hashes. new_txlist_hash, found_txlist_hash = check.consensus_hash(db, 'txlist_hash', previous_txlist_hash, txlist) new_ledger_hash, found_ledger_hash = check.consensus_hash(db, 'ledger_hash', previous_ledger_hash, util.BLOCK_LEDGER) new_messages_hash, found_messages_hash = check.consensus_haM sh(db, 'messages_hash', previous_messages_hash, database.BLOCK_MESSAGES) return new_ledger_hash, new_txlist_hash, new_messages_hash, found_messages_hash """Initialise data, create and populate the database.""" cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS blocks( block_index INTEGER UNIQUE, block_hash TEXT UNIQUE, block_time INTEGER, previous_block_hash TEXM difficulty INTEGER, PRIMARY KEY (block_index, block_hash)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON blocks (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS index_hash_idx ON blocks (block_index, block_hash) ''') t do `ALTER TABLE IF COLUMN NOT EXISTS`. columns = [column['name'] for column M in cursor.execute('''PRAGMA table_info(blocks)''')] if 'ledger_hash' not in columns: cursor.execute('''ALTER TABLE blocks ADD COLUMN ledger_hash TEXT''') if 'txlist_hash' not in columns: cursor.execute('''ALTER TABLE blocks ADD COLUMN txlist_hash TEXT''') if 'messages_hash' not in columns: cursor.execute('''ALTER TABLE blocks ADD COLUMN messages_hash TEXT''') if 'previous_block_hash' not in columns: cursor.execute('''ALTER TABLE blocks ADD COLUMN previous_block_hash TM if 'difficulty' not in columns: cursor.execute('''ALTER TABLE blocks ADD COLUMN difficulty TEXT''') # Check that first block in DB is BLOCK_FIRST. cursor.execute('''SELECT * from blocks ORDER BY block_index''') blocks = list(cursor) if blocks[0]['block_index'] != config.BLOCK_FIRST: raise exceptions.DatabaseError('First block in database is not block {}.'.format(config.BLOCK_FIRST)) cursor.execute('''CREATE TABLE IF NOTM EXISTS transactions( tx_index INTEGER UNIQUE, tx_hash TEXT UNIQUE, block_index INTEGER, block_hash TEXT, block_time INTEGER, source TEXT, destination TEXT, btc_amount INTEGER, fee INTEGER, data BLOB, supported BOOL DEFAULT 1, FOREIGN KEY (block_index, bloM ck_hash) REFERENCES blocks(block_index, block_hash), PRIMARY KEY (tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON transactions (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx_index_idx ON transactions (tx_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx_hash_idx ON transactionsM ''') cursor.execute('''CREATE INDEX IF NOT EXISTS index_index_idx ON transactions (block_index, tx_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS index_hash_index_idx ON transactions (tx_index, tx_hash, block_index) ''') # Purge database of blocks, transactions from before BLOCK_FIRST. cursor.execute('''DELETE FROM blocks WHERE block_index < ?''', (config.BLOCK_FIRST,)) or.execute('''DELETE FROM transactions WHERE block_index < ?''', (config.BLOCK_FIRST,)) # (Valid) debits cursor.execute('''CREATE TABLE IF NOT EXISTS debits( block_index INTEGER, address TEXT, asset TEXT, quantity INTEGER, action TEXT, event TEXT, FOREIGN KEY (block_index) REFERENCES blocks(block_index)) ''') cursor.execute('''CREAM TE INDEX IF NOT EXISTS address_idx ON debits (address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS asset_idx ON debits (asset) ''') # (Valid) credits cursor.execute('''CREATE TABLE IF NOT EXISTS credits( block_index INTEGER, address TEXT, asset TEXT, quantity INTEGER, calling_function TEXT, FOREIGN KEY (block_index) REFERENCES blocks(block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS address_idx ON credits (address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS asset_idx ON credits (asset) ''') cursor.execute('''CREATE TABLE IF NOT EXISTS balances( address TEXT, asset TEXT, quantity INTEGER) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS address_asset_idx ON balances (address, asset) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS address_idx ON balances (address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS asset_idx ON balances (asset) ''') # TODO: Store more asset info here?! sor.execute('''CREATE TABLE IF NOT EXISTS assets( asset_id TEXT UNIQUE, asset_name TEXT UNIQUE, block_index INTEGER, asset_longname TEXT) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS name_idx ON assets (asset_name) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS id_idx ON assets (asset_id) ''') # Add asset_lonM gname for sub-assets t do `ALTER TABLE IF COLUMN NOT EXISTS`. columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(assets)''')] if 'asset_longname' not in columns: cursor.execute('''ALTER TABLE assets ADD COLUMN asset_longname TEXT''') cursor.execute('''CREATE UNIQUE INDEX IF NOT EXISTS asset_longname_idx ON assets(asset_longname)''') cursor.execute('''SELECT * FROM assets WHERE asset_name = ?''', ('BTC',)) if not list(cursor): execute('''INSERT INTO assets VALUES (?,?,?,?)''', ('0', 'BTC', None, None)) cursor.execute('''INSERT INTO assets VALUES (?,?,?,?)''', ('1', 'XCP', None, None)) # Leaving this here because in the future this could work for other things besides broadcast cursor.execute('''CREATE TABLE IF NOT EXISTS addresses( address TEXT UNIQUE, options INTEGER, block_index INTEGER) ''') cursor.execute('''CREATEM INDEX IF NOT EXISTS addresses_idx ON addresses (address) ''') send.initialise(db) destroy.initialise(db) order.initialise(db) btcpay.initialise(db) issuance.initialise(db) broadcast.initialise(db) bet.initialise(db) dividend.initialise(db) burn.initialise(db) cancel.initialise(db) rps.initialise(db) rpsresolve.initialise(db) sweep.initialise(db) dispenser.initialise(db) ecute('''CREATE TABLE IF NOT EXISTS messages( message_index INTEGER PRIMARY KEY, block_index INTEGER, command TEXT, category TEXT, bindings TEXT, timestamp INTEGER) ''') # TODO: FOREIGN KEY (block_index) REFERENCES blocks(block_index) DEFERRABLE INITIALLY DEFERRED) cursor.execute('''CREATE INDEX IF NOT EXISTS block_indexM _idx ON messages (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_message_index_idx ON messages (block_index, message_index) ''') # Create undolog tables cursor.execute('''CREATE TABLE IF NOT EXISTS undolog( undo_index INTEGER PRIMARY KEY AUTOINCREMENT, sql TEXT) ''') cursor.execute('''CREATE TABLE IF NOT EXISTS undolog_block( M block_index INTEGER PRIMARY KEY, first_undo_index INTEGER) ''') # Create undolog triggers for all tables in TABLES list, plus the 'balances' table for table in UNDOLOG_TABLES: columns = [column['name'] for column in cursor.execute('''PRAGMA table_info({})'''.format(table))] cursor.execute('''CREATE TRIGGER IF NOT EXISTS _{}_insert AFTER INSERT ON {} BEGIN INSERT INTO undolog VALUES(NULL, 'DELETE FROM {} WHERE rowid='|M END; '''.format(table, table, table)) columns_parts = ["{}='||quote(old.{})||'".format(c, c) for c in columns] cursor.execute('''CREATE TRIGGER IF NOT EXISTS _{}_update AFTER UPDATE ON {} BEGIN INSERT INTO undolog VALUES(NULL, 'UPDATE {} SET {} WHERE rowid='||old.rowid); END; '''.format(table, table, table, ','.join(columns_parts))) columns_parts = M ["'||quote(old.{})||'".format(c) for c in columns] cursor.execute('''CREATE TRIGGER IF NOT EXISTS _{}_delete BEFORE DELETE ON {} BEGIN INSERT INTO undolog VALUES(NULL, 'INSERT INTO {}(rowid,{}) VALUES('||old.rowid||',{})'); END; '''.format(table, table, table, ','.join(columns), ','.join(columns_parts))) # Drop undolog tables on messages table if they exist (fix for adding them in 9.52.0) for trigger_type in ('insert', 'uM cursor.execute("DROP TRIGGER IF EXISTS _messages_{}".format(trigger_type)) # Mempool messages # NOTE: `status`, 'block_index` are removed from bindings. cursor.execute('''DROP TABLE IF EXISTS mempool''') cursor.execute('''CREATE TABLE mempool( tx_hash TEXT, command TEXT, category TEXT, bindings TEXT, timestamp INTEGER) ''') ef get_tx_info(tx_hex, block_parser=None, block_index=None, db=None): """Get the transaction info. Returns normalized None data for DecodeError and BTCOnlyError.""" return _get_tx_info(tx_hex, block_parser, block_index) except DecodeError as e: return b'', None, None, None, None, None except BTCOnlyError as e: # NOTE: For debugging, logger.debug('Could not decode: ' + str(e)) if util.enabled('dispensers', block_index): return b'',M None, None, None, None, _get_swap_tx(e.decodedTx, block_parser, block_index, db=db) except: # (DecodeError, backend.indexd.BackendRPCError) as e: return b'', None, None, None, None, None return b'', None, None, None, None, None def _get_swap_tx(decoded_tx, block_parser=None, block_index=None, db=None): def get_pubkeyhash(scriptpubkey): asm = script.get_asm(scriptpubkey) if len(asm) > 0: if asm[0] == "OP_DUP": if len(asm) != 5 or asm[1] != 'OP_HASH160' or asm[3] != 'OP_EQUALVERIFY' or asm[4] != 'OP_CHECKSIG': return False else: return {"pubkeyhash":asm[2],"address_version":config.ADDRESSVERSION} elif (asm[0] == "OP_HASH160") and util.enabled('p2sh_dispensers_support'): if len(asm) != 3 or asm[-1] != 'OP_EQUAL': return False else: return {"pubkeyhash":asm[1M ],"address_version":config.P2SH_ADDRESSVERSION} return False def get_address(scriptpubkey): if util.enabled('correct_segwit_txids') and scriptpubkey.is_witness_v0_keyhash(): pubkey = scriptpubkey[2:] address = str(bitcoinlib.bech32.CBech32Data.from_bytes(0, pubkey)) return address pubkeyhashdict = get_pubkeyhash(scriptpubkey) if not pubkeyhashdict: return False pubkeyhash = pubM keyhashdict["pubkeyhash"] address_version = pubkeyhashdict["address_version"] pubkeyhash = binascii.hexlify(pubkeyhash).decode('utf-8') address = script.base58_check_encode(pubkeyhash, address_version) # Test decoding of address. if address != config.UNSPENDABLE and binascii.unhexlify(bytes(pubkeyhash, 'utf-8')) != script.base58_check_decode(address, address_version): return False return address check_sources = db == None # If we didn't get passed a database cursor, assume we have to check for dispenser for vout in decoded_tx.vout: address = get_address(vout.scriptPubKey) destination = None btc_amount = None destination = address btc_amount = vout.nValue elif util.enabled('hotfix_dispensers_with_non_p2pkh'): asm = script.get_asm(vout.scriptPubKey) if asm[-1] == 'OP_CHECKSIG': destinatiM on, new_data = decode_checksig(asm, decoded_tx) elif asm[-1] == 'OP_CHECKMULTISIG': destination, new_data = decode_checkmultisig(asm, decoded_tx) elif asm[0] == 'OP_HASH160' and asm[-1] == 'OP_EQUAL' and len(asm) == 3: destination, new_data = decode_scripthash(asm) elif asm[0] == 'OP_RETURN': pass #Just ignore. elif util.enabled('segwit_support') and asm[0] == 0: # Segwit output destinatioM n, new_data = decode_p2w(vout.scriptPubKey) logger.error('unrecognised scriptPubkey. Just ignore this: ' + str(asm)) if destination and not new_data: amount = vout.nValue logger.error('cannot parse destination address or new_data found: ' + str(asm)) if db != None and dispenser.is_dispensable(db, destination, btc_amount): check_sources = True outputs.append((destination, btc_amount)) # Collect all (unique) source addresses. # if we haven't found them yet if check_sources: for vin in decoded_tx.vin[:]: # Loop through inputs. if block_parser: vin_tx = block_parser.read_raw_transaction(ib2h(vin.prevout.hash)) vin_ctx = backend.deserialize(vin_tx['__data__']) vin_tx = backend.getrawtransaction(ib2h(vin.prevout.hash)) # TODO: Biggest penalty on parsing is here vin_ctx = backend.deserialize(vin_tx) vout = vin_ctx.vout[vin.prevout.n] asm = script.get_asm(vout.scriptPubKey) if asm[-1] == 'OP_CHECKSIG': new_source, new_data = decode_checksig(asm, decoded_tx) if new_data or not new_source: raise DecodeError('data in source') elif asm[-1] == 'OP_CHECKMULTISIG': new_source, new_data = decode_checkmultisig(asm, decoded_tx) if new_data or noM raise DecodeError('data in source') elif asm[0] == 'OP_HASH160' and asm[-1] == 'OP_EQUAL' and len(asm) == 3: new_source, new_data = decode_scripthash(asm) if new_data or not new_source: raise DecodeError('data in source') elif util.enabled('segwit_support') and asm[0] == 0: # Segwit output # Get the full transaction data for this input transaction. new_source,M new_data = decode_p2w(vout.scriptPubKey) raise DecodeError('unrecognised source type') # old; append to sources, results in invalid addresses # new; first found source is source, the rest can be anything (to fund the TX for example) if not (util.enabled('first_input_is_source') and len(sources)): # Collect unique sources. if new_source not in sources: sources.append(new_source) def _get_tx_info(tx_hex, block_parser=None, block_index=None, p2sh_is_segwit=False): """Get the transaction info. Calls one of two subfunctions depending on signature type.""" if not block_index: block_index = util.CURRENT_BLOCK_INDEX if util.enabled('p2sh_addresses', block_index=block_index): # Protocol change. return get_tx_info3(tx_hex, block_parser=block_parser, p2sh_is_segwit=p2sh_is_segwit) elif util.enabled('multisig_addresses', block_index=block_index): M return get_tx_info2(tx_hex, block_parser=block_parser) return get_tx_info1(tx_hex, block_index, block_parser=block_parser) def get_tx_info1(tx_hex, block_index, block_parser=None): """Get singlesig transaction info. The destination, if it exists, always comes before the data output; the change, if it exists, always comes after. ctx = backend.deserialize(tx_hex) def get_pubkeyhash(scriptpubkey): asm = script.get_asm(scriptpubkey) if len(asm) != 5 or asm[0] != 'OP_DUP' or asm[1] != 'OP_HASH160' or asm[3] != 'OP_EQUALVERIFY' or asm[4] != 'OP_CHECKSIG': return False return asm[2] def get_address(scriptpubkey): pubkeyhash = get_pubkeyhash(scriptpubkey) if not pubkeyhash: return False pubkeyhash = binascii.hexlify(pubkeyhash).decode('utf-8') address = script.base58_check_encode(pubkeyhash, config.ADDRESSVERSION) # Test decoding of address. if address != confM ig.UNSPENDABLE and binascii.unhexlify(bytes(pubkeyhash, 'utf-8')) != script.base58_check_decode(address, config.ADDRESSVERSION): return False return address # Fee is the input values minus output values. # Get destination output and data output. destination, btc_amount, data = None, None, b'' pubkeyhash_encoding = False for vout in ctx.vout: fee -= vout.nValue # Sum data chunks to get data. (Can mix OP_RETURN and multi-sig.) asm = scripM t.get_asm(vout.scriptPubKey) if len(asm) == 2 and asm[0] == 'OP_RETURN': # OP_RETURN if type(asm[1]) != bytes: continue data_chunk = asm[1] data += data_chunk elif len(asm) == 5 and asm[0] == 1 and asm[3] == 2 and asm[4] == 'OP_CHECKMULTISIG': # Multi-sig if type(asm[2]) != bytes: continue data_pubkey = asm[2] data_chunk_length = data_pubkey[0] # No M data_chunk = data_pubkey[1:data_chunk_length + 1] data += data_chunk elif len(asm) == 5 and (block_index >= 293000 or config.TESTNET or config.REGTEST): # Protocol change. # Be strict. pubkeyhash = get_pubkeyhash(vout.scriptPubKey) if not pubkeyhash: continue if ctx.is_coinbase(): raise DecodeError('coinbase transaction') obj1 = arc4.init_arc4(ctx.vin[0].prevout.hash[::-1]M data_pubkey = obj1.decrypt(pubkeyhash) if data_pubkey[1:9] == config.PREFIX or pubkeyhash_encoding: pubkeyhash_encoding = True data_chunk_length = data_pubkey[0] # No ord() necessary. data_chunk = data_pubkey[1:data_chunk_length + 1] if data_chunk[-8:] == config.PREFIX: data += data_chunk[:-8] break else: data += data_chunk # Destination is tM he first output before the data. if not destination and not btc_amount and not data: address = get_address(vout.scriptPubKey) if address: destination = address btc_amount = vout.nValue # Check for, and strip away, prefix (except for burns). if destination == config.UNSPENDABLE: elif data[:len(config.PREFIX)] == config.PREFIX: data = data[len(config.PREFIX):] raise DecodeError('no prefix') look for source if data were found or destination is UNSPENDABLE, for speed. if not data and destination != config.UNSPENDABLE: raise BTCOnlyError('no data and not unspendable') # Collect all possible source addresses; ignore coinbase transactions and anything but the simplest Pay source_list = [] for vin in ctx.vin[:]: # Loop through input transactions. if vin.prevout.is_null(): raise DecodeError('cM oinbase transaction') # Get the full transaction data for this input transaction. if block_parser: vin_tx = block_parser.read_raw_transaction(ib2h(vin.prevout.hash)) vin_ctx = backend.deserialize(vin_tx['__data__']) vin_tx = backend.getrawtransaction(ib2h(vin.prevout.hash)) vin_ctx = backend.deserialize(vin_tx) vout = vin_ctx.vout[vin.prevout.n] fee += vout.nValue address = get_address(vout.scriptPubKey) raise DecodeError('invalid scriptpubkey') source_list.append(address) # Require that all possible source addresses be the same. if all(x == source_list[0] for x in source_list): source = source_list[0] source = None return source, destination, btc_amount, fee, data, None def get_tx_info3(tx_hex, block_parser=None, p2sh_is_segwit=False): return get_tx_info2(tx_hex, block_parser=block_parser, p2sh_support=True, p2sh_is_M segwit=p2sh_is_segwit) def arc4_decrypt(cyphertext, ctx): obfuscate. Initialise key once per attempt.''' key = arc4.init_arc4(ctx.vin[0].prevout.hash[::-1]) return key.decrypt(cyphertext) def get_opreturn(asm): if len(asm) == 2 and asm[0] == 'OP_RETURN': pubkeyhash = asm[1] if type(pubkeyhash) == bytes: return pubkeyhash raise DecodeError('invalid OP_RETURN') def decode_opreturn(asm, ctx): chunk = get_opreturn(asm) chunk = arc4_decrypt(chunk, ctx) if chunk[:len(config.PREFIX)] == config.PREFIX: # Data destination, data = None, chunk[len(config.PREFIX):] raise DecodeError('unrecognised OP_RETURN output') return destination, data def decode_checksig(asm, ctx): pubkeyhash = script.get_checksig(asm) chunk = arc4_decrypt(pubkeyhash, ctx) if chunk[1:len(config.PREFIX) + 1] == config.PREFIX: # Data # Padding byte in each output (instead of just in the last one) so that encoding methods may beM s just not very much data. chunk_length = chunk[0] chunk = chunk[1:chunk_length + 1] destination, data = None, chunk[len(config.PREFIX):] else: # Destination pubkeyhash = binascii.hexlify(pubkeyhash).decode('utf-8') destination, data = script.base58_check_encode(pubkeyhash, config.ADDRESSVERSION), None return destination, data def decode_scripthash(asm): destination = script.base58_check_enM code(binascii.hexlify(asm[1]).decode('utf-8'), config.P2SH_ADDRESSVERSION) return destination, None def decode_checkmultisig(asm, ctx): pubkeys, signatures_required = script.get_checkmultisig(asm) for pubkey in pubkeys[:-1]: # (No data in last pubkey.) chunk += pubkey[1:-1] # Skip sign byte and nonce byte. chunk = arc4_decrypt(chunk, ctx) if chunk[1:len(config.PREFIX) + 1] == config.PREFIX: # Data # Padding byte in each output (instead of just iM n the last one) so that encoding methods may be mixed. Also, it s just not very much data. chunk_length = chunk[0] chunk = chunk[1:chunk_length + 1] destination, data = None, chunk[len(config.PREFIX):] else: # Destination pubkeyhashes = [script.pubkey_to_pubkeyhash(pubkey) for pubkey in pubkeys] destination, data = script.construct_array(signatures_required, pubkeyhashes, len(pubkeyhashes)), None return destinatM def decode_p2w(script_pubkey): bech32 = bitcoinlib.bech32.CBech32Data.from_bytes(0, script_pubkey[2:22]) return str(bech32), None except TypeError as e: raise DecodeError('bech32 decoding error') def get_tx_info2(tx_hex, block_parser=None, p2sh_support=False, p2sh_is_segwit=False): """Get multisig transaction info. The destinations, if they exists, always comes before the data output; the change, if it exists, always comes after. ctx = backend.deserialize(tx_hex) # Ignore coinbase transactions. if ctx.is_coinbase(): raise DecodeError('coinbase transaction') # Get destinations and data outputs. destinations, btc_amount, fee, data = [], 0, 0, b'' for vout in ctx.vout: # Fee is the input values minus output values. output_value = vout.nValue fee -= output_value # Ignore transactions with invalid script. asm = script.get_asm(vout.scriptPM except CScriptInvalidError as e: raise DecodeError(e) if asm[0] == 'OP_RETURN': new_destination, new_data = decode_opreturn(asm, ctx) elif asm[-1] == 'OP_CHECKSIG': new_destination, new_data = decode_checksig(asm, ctx) elif asm[-1] == 'OP_CHECKMULTISIG': new_destination, new_data = decode_checkmultisig(asm, ctx) raise DecodeError('unrecognised output type') sh_support and asm[0] == 'OP_HASH160' and asm[-1] == 'OP_EQUAL' and len(asm) == 3: new_destination, new_data = decode_scripthash(asm) elif util.enabled('segwit_support') and asm[0] == 0: # Segwit Vout, second param is redeemScript #redeemScript = asm[1] new_destination, new_data = decode_p2w(vout.scriptPubKey) raise DecodeError('unrecognised output type') assert not (new_destination and new_data) assert new_destinationM != None or new_data != None # `decode_*()` should never return `None, None`. if util.enabled('null_data_check'): if new_data == []: raise DecodeError('new destination is `None`') # All destinations come before all data. if not data and not new_data and destinations != [config.UNSPENDABLE,]: destinations.append(new_destination) btc_amount += output_value if new_destination: # Change. break else: # Data. data += new_data # source can be determined by parsing the p2sh_data transaction # or from the first spent output # P2SH encoding signalling p2sh_encoding_source = None if util.enabled('p2sh_encoding') and data == b'P2SH': for vin in ctx.vin: if util.enabled("prevout_segwit_fix"): vin_tx = backend.getrawtransaction(ib2h(vin.prevout.hash)) vin_ctx =M backend.deserialize(vin_tx) prevout_is_segwit = vin_ctx.has_witness() prevout_is_segwit = p2sh_is_segwit # Ignore transactions with invalid script. asm = script.get_asm(vin.scriptSig) except CScriptInvalidError as e: raise DecodeError(e) new_source, new_destination, new_data = p2sh_encoding.decode_p2sh_input(asm, p2sh_is_segwit=prevout_is_segwit) s could be a p2sh source address with no encoded data if new_data is None: continue; if new_source is not None: if p2sh_encoding_source is not None and new_source != p2sh_encoding_source: # this p2sh data input has a bad source address raise DecodeError('inconsistent p2sh inputs') p2sh_encoding_source = new_source assert not new_destination data += new_data # Only look for soM urce if data were found or destination is `UNSPENDABLE`, if not data and destinations != [config.UNSPENDABLE,]: raise BTCOnlyError('no data and not unspendable', ctx) # Collect all (unique) source addresses. # if we haven't found them yet for vin in ctx.vin[:]: # Loop through inputs. # Get the full transaction data for this input transaction. if block_parser: vin_tx = block_parser.read_raw_transaction(ib2h(vin.prevout.hash)) vin_ctx = backend.deserialize(vin_tx['__data__']) vin_tx = backend.getrawtransaction(ib2h(vin.prevout.hash)) vin_ctx = backend.deserialize(vin_tx) vout = vin_ctx.vout[vin.prevout.n] fee += vout.nValue asm = script.get_asm(vout.scriptPubKey) if asm[-1] == 'OP_CHECKSIG': new_source, new_data = decode_checksig(asm, ctx) if new_data or not new_source: raise DecodeError('data in source') asm[-1] == 'OP_CHECKMULTISIG': new_source, new_data = decode_checkmultisig(asm, ctx) if new_data or not new_source: raise DecodeError('data in source') elif p2sh_support and asm[0] == 'OP_HASH160' and asm[-1] == 'OP_EQUAL' and len(asm) == 3: new_source, new_data = decode_scripthash(asm) if new_data or not new_source: raise DecodeError('data in source') elif util.enabled('segwit_support') and asm[0] == 0: new_source, new_data = decode_p2w(vout.scriptPubKey) raise DecodeError('unrecognised source type') # old; append to sources, results in invalid addresses # new; first found source is source, the rest can be anything (to fund the TX for example) if not (util.enabled('first_input_is_source') and len(sources)): # Collect unique sources. if new_source not in sources: sources.append(new_source) the source from the p2sh data source if p2sh_encoding_source is not None: sources = p2sh_encoding_source sources = '-'.join(sources) destinations = '-'.join(destinations) return sources, destinations, btc_amount, round(fee), data, None def reinitialise(db, block_index=None): """Drop all predefined tables and initialise the database once again.""" cursor = db.cursor() # Delete all of the results of parsing (including the undolog) for table in TABLES + ['balancM es', 'undolog', 'undolog_block']: cursor.execute('''DROP TABLE IF EXISTS {}'''.format(table)) # Create missing tables # clean consensus hashes if first block hash doesn't match with checkpoint. if config.TESTNET: checkpoints = check.CHECKPOINTS_TESTNET elif config.REGTEST: checkpoints = check.CHECKPOINTS_REGTEST checkpoints = check.CHECKPOINTS_MAINNET columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(blocksM for field in ['ledger_hash', 'txlist_hash']: if field in columns: sql = '''SELECT {} FROM blocks WHERE block_index = ?'''.format(field) first_block = list(cursor.execute(sql, (config.BLOCK_FIRST,))) if first_block: first_hash = first_block[0][field] if first_hash != checkpoints[config.BLOCK_FIRST][field]: logger.info('First hash changed. Cleaning {}.'.format(field)) cursor.execute('''UPDATM E blocks SET {} = NULL'''.format(field)) # For rollbacks, just delete new blocks and then reparse what cursor.execute('''DELETE FROM transactions WHERE block_index > ?''', (block_index,)) cursor.execute('''DELETE FROM blocks WHERE block_index > ?''', (block_index,)) elif config.TESTNET or config.REGTEST: # block_index NOT specified and we are running testnet # just blow away the consensus hashes with a full testnet reparse, as we could activate # new features retroactively, which could otherwise lead to ConsensusError exceptions being raised. logger.info("Testnet/regtest full reparse detected: Clearing all consensus hashes before performing reparse.") cursor.execute('''UPDATE blocks SET ledger_hash = NULL, txlist_hash = NULL, messages_hash = NULL''') def reparse(db, block_index=None, quiet=False): """Reparse all transactions (atomically). If block_index is set, rollback to the end of that block. f reparse_from_undolog(db, block_index, quiet): """speedy reparse method that utilizes the undolog. if fails, fallback to the full reparse method""" if not block_index: return False # Can't reparse from undolog undolog_cursor = db.cursor() undolog_cursor.setexectrace(None) undolog_cursor.setrowtrace(None) def get_block_index_for_undo_index(undo_indexes, undo_index): for block_index, first_undo_index in undo_indexes.items(): #in order if undo_index < first_undo_index: return block_index - 1 return next(reversed(undo_indexes)) #the last inserted block_index # Check if we can reparse from the undolog results = list(undolog_cursor.execute( '''SELECT block_index, first_undo_index FROM undolog_block WHERE block_index >= ? ORDER BY block_index ASC''', (block_index,))) undo_indexes = collections.OrderedDict() for result in results: undo_indexes[result[0]] = result[1] undo_start_block_index = block_index + 1 if undo_start_block_index not in undo_indexes: if block_index in undo_indexes: # Edge case, should only happen if we're "rolling back" to latest block (e.g. via cmd line) return True #skip undo else: return False # Undolog doesn't go that far back, full reparse required... # Grab the undolog... undolog = list(undolog_cursor.execute( '''SELECT undo_index, sql FROM undolog WHERE undo_index >= ? ORDER BY undo_index DESC''', (undo_indexes[undo_start_block_index],))) # Replay the undolog backwards, from the last entry to first_undo_index... for entry in undolog: logger.info("Undolog: Block {} (undo_index {}): {}".format( get_block_index_for_undo_index(undo_indexes, entry[0]), eM undolog_cursor.execute(entry[1]) # Trim back tx and blocks undolog_cursor.execute('''DELETE FROM transactions WHERE block_index > ?''', (block_index,)) undolog_cursor.execute('''DELETE FROM blocks WHERE block_index > ?''', (block_index,)) # As well as undolog entries... undolog_cursor.execute('''DELETE FROM undolog WHERE undo_index >= ?''', (undo_indexes[undo_start_block_index],)) undolog_cursor.execute('''DM ELETE FROM undolog_block WHERE block_index >= ?''', (undo_start_block_index,)) undolog_cursor.close() logger.info('Rolling back transactions to block {}.'.format(block_index)) logger.info('Reparsing all transactions.') check.software_version() reparse_start = time.time() # Reparse from the undolog if possible reparsed = reparse_from_undolog(db, block_index, quiet) cursor = db.cursor() if not reparsed: logger.info("Could not roll back from undolog. Performing full reparse instead...") root_logger = logging.getLogger() root_level = logger.getEffectiveLevel() reinitialise(db, block_index) # Reparse all blocks, transactions. if quiet: root_logger.setLevel(logging.WARNING) previous_ledger_hash, previous_txlist_hash, previous_messages_hash = None, None, None cursor.execute('''SELECT * FROM blocks ORDER BY block_index''') for block in cursor.fetchall(): util.CURRENT_BLOCK_INDEX = block['block_index'] previous_ledger_hash, previous_txlist_hash, previous_messages_hash, previous_found_messages_hash = parse_block( db, block['block_index'], block['block_time'], previous_ledger_hash=prevM previous_txlist_hash=previous_txlist_hash, previous_messages_hash=previous_messages_hash) if quiet and block['block_index'] % 10 == 0: # every 10 blocks print status root_logger.setLevel(logging.INFO) logger.info('Block (re-parse): %s (hashes: L:%s / TX:%s / M:%s%s)' % ( block['blocM k_index'], previous_ledger_hash[-5:], previous_txlist_hash[-5:], previous_messages_hash[-5:], (' [overwrote %s]' % previous_found_messages_hash) if previous_found_messages_hash and previous_found_messages_hash != previous_messages_hash else '')) if quiet and block['block_index'] % 10 == 0: root_logger.setLevel(logging.WARNING) root_logger.setLevel(root_level) # Check for conservation of assets. .asset_conservation(db) # Update database version number. database.update_version(db) reparse_end = time.time() logger.info("Reparse took {:.3f} minutes.".format((reparse_end - reparse_start) / 60.0)) # on full reparse - vacuum the DB afterwards for better subsequent performance (especially on non-SSDs) if not block_index: database.vacuum(db) def list_tx(db, block_hash, block_index, block_time, tx_hash, tx_index, tx_hex=None): assert type(tx_hash) =M cursor = db.cursor() # Edge case: confirmed tx_hash also in mempool cursor.execute('''SELECT * FROM transactions WHERE tx_hash = ?''', (tx_hash,)) transactions = list(cursor) if transactions: return tx_index # Get the important details about each transaction. if tx_hex is None: tx_hex = backend.getrawtransaction(tx_hash) # TODO: This is the call that is stalling the process the most source, destination, btc_amount, fee, data, decoded_tx = get_tx_info(tx_hex, M if not source and decoded_tx and util.enabled('dispensers', block_index): outputs = decoded_tx[1] for out in outputs: if out[0] != decoded_tx[0][0] and dispenser.is_dispensable(db, out[0], out[1]): source = decoded_tx[0][0] destination = out[0] btc_amount = out[1] fee = 0 data = struct.pack(config.SHORT_TXTYPE_FORMAT, dispenser.DISPENSE_ID) data += b'\x00' break # PM revent inspection of further dispenses (only first one is valid) if block_hash == None: block_hash = config.MEMPOOL_BLOCK_HASH block_index = config.MEMPOOL_BLOCK_INDEX assert block_index == util.CURRENT_BLOCK_INDEX if source and (data or destination == config.UNSPENDABLE or decoded_tx): logger.debug('Saving transaction: {}'.format(tx_hash)) cursor.execute('''INSERT INTO transactions( tx_index, block_index, block_hash, block_time, source, destination, btc_amount, fee, data) VALUES(?,?,?,?,?,?,?,?,?,?)''', (tx_index, tx_hash, block_index, block_hash, block_time, source, destination, btc_amount, fee, data) ) cursor.close() return tx_index + 1 logger.getChild('list_tx.skip').debug('Skipping transaction: {}'.format(tx_hash)) def kickstart(db, bitcoind_dir): if bitcoind_dir is None: if platform.system() == 'DM bitcoind_dir = os.path.expanduser('~/Library/Application Support/Bitcoin/') elif platform.system() == 'Windows': bitcoind_dir = os.path.join(os.environ['APPDATA'], 'Bitcoin') bitcoind_dir = os.path.expanduser('~/.bitcoin') if not os.path.isdir(bitcoind_dir): raise Exception('Bitcoin Core data directory not found at {}. Use --bitcoind-dir parameter.'.format(bitcoind_dir)) cursor = db.cursor() logger.warning('''Warning: hat bitcoind is stopped. - You must reindex bitcoind after the initialization is complete (restart with `-reindex=1`) - The initialization may take a while.''') if input('Proceed with the initialization? (y/N) : ') != 'y': if config.TESTNET: first_hash = config.BLOCK_FIRST_TESTNET_HASH elif config.REGTEST: first_hash = config.BLOCK_FIRST_REGTEST_HASH first_hash = config.BLOCK_FIRST_MAINNET_HASH start_time_total = time.time() # Get hash of last M chain_parser = ChainstateParser(os.path.join(bitcoind_dir, 'chainstate')) last_hash = chain_parser.get_last_block_hash() chain_parser.close() # Start block parser. block_parser = BlockchainParser(os.path.join(bitcoind_dir, 'blocks'), os.path.join(bitcoind_dir, 'blocks/index')) current_hash = last_hash # Prepare SQLite database. # TODO: Be more specific! logger.info('Preparing database.') start_time = time.time() nitialise(db, block_index=config.BLOCK_FIRST - 1) logger.info('Prepared database in {:.3f}s'.format(time.time() - start_time)) # Get blocks and transactions, moving backwards in time. while current_hash != None: start_time = time.time() transactions = [] # Get `tx_info`s for transactions in this block. block = block_parser.read_raw_block(current_hash) for tx in block['transactions']: source, destination, btc_amountM , fee, data = get_tx_info(tx['__data__'], block_parser=block_parser, block_index=block['block_index']) if source and (data or destination == config.UNSPENDABLE): transactions.append(( tx['tx_hash'], block['block_index'], block['block_hash'], block['block_time'], source, destination, btc_amount, fee, data )) logger.info('Valid transaction: {}'.format(tx['tx_hash'])) # Insert block M and transactions into database. cursor.execute('''INSERT INTO blocks( block_index, block_hash, block_time) VALUES(?,?,?)''', (block['block_index'], block['block_hash'], block['block_time'])) if len(transactions): transactions = list(reversed(transactions)) tx_chunks = [transactions[i:i+90] for i in range(0, len(transactions), 90)] for tx_chunk in tx_chunks: sql = '''INSERT INTO transactions (tx_index, tx_hash, block_index, block_hash, block_time, source, destination, btc_amount, fee, data) VALUES ''' bindings = () bindings_place = [] # negative tx_index from -1 and inverse order for fast reordering # TM ODO: Can this be clearer? for tx in tx_chunk: bindings += (-(tx_index + 1),) + tx bindings_place.append('''(?,?,?,?,?,?,?,?,?,?)''') tx_index += 1 sql += ', '.join(bindings_place) cursor.execute(sql, bindings) logger.info('Block {} ({}): {}/{} saved in {:.3f}s'.format( block['block_index'], block['block_hash'], len(transaM ctions), len(block['transactions']), time.time() - start_time)) # Get hash of next block. current_hash = block['hash_prev'] if current_hash != first_hash else None block_parser.close() # Reorder all transactions in database. logger.info('Reordering transactions.') start_time = time.time() cursor.execute('''UPDATE transactions SET tx_index = tx_index + ?''', (tx_index,)) logger.info('Reordered transactions in {:.3f}sM .'.format(time.time() - start_time)) # Parse all transactions in database. logger.info('Total duration: {:.3f}s'.format(time.time() - start_time_total)) def last_db_index(db): cursor = db.cursor() blocks = list(cursor.execute('''SELECT * FROM blocks WHERE block_index = (SELECT MAX(block_index) from blocks)''')) return blocks[0]['block_index'] except IndexError: return 0 except apsw.SQLError: def get_next_tx_index(db): """Return index of next transaction.""" cursor = db.cursor() txes = list(cursor.execute('''SELECT * FROM transactions WHERE tx_index = (SELECT MAX(tx_index) from transactions)''')) assert len(txes) == 1 tx_index = txes[0]['tx_index'] + 1 tx_index = 0 class MempoolError(Exception): # Check software version. check.software_version() # Get index of last block. if util.CURRENT_BLOCK_INDEX == 0: logger.warning('New database.') block_index = config.BLOCK_FIRST block_index = util.CURRENT_BLOCK_INDEX + 1 # Check database version. check.database_version(db) except check.DatabaseVersionError as e: logger.info(str(e)) # no need to reparse or rollback a new database if block_index != config.BLOCK_FIRST: reparse(db, block_index=e.reparse_block_index, quiet=False) else: #version update was included in reparse(), so don't do it twice database.update_version(db) logger.info('Resuming parsing.') # Get index of last transaction. tx_index = get_next_tx_index(db) not_supported = {} # No false positives. Use a dict to allow for O(1) lookups not_supported_sorted = collections.deque() # ^ Entries in form of (block_index, tx_hash), oldest first. Allows for easy reM moval of past, unncessary entries cursor = db.cursor() # a reorg can happen without the block count increasing, or even for that # matter, with the block count decreasing. This should only delay # processing of the new blocks a bit. start_time = time.time() # Get block count. # If the backend is unreachable and `config.FORCE` is set, just sleep # and try again repeatedly. block_count = backend.getblockcount() onnectionRefusedError, http.client.CannotSendRequest, backend.addrindexrs.BackendRPCError) as e: if config.FORCE: time.sleep(config.BACKEND_POLL_INTERVAL) continue raise e # Get new blocks. if block_index <= block_count: current_index = block_index # Backwards check for incorrect blocks due to chain reorganisation, and stop when a common parent is found. if block_count - block_index < M 100: # Undolog only saves last 100 blocks, if there's a reorg deeper than that manual reparse should be done requires_rollback = False while True: if current_index == config.BLOCK_FIRST: break logger.debug('Checking that block {} is not an orphan.'.format(current_index)) # Backend parent hash. current_hash = backend.getblockhash(current_index) current_cblock = M backend.getblock(current_hash) backend_parent = bitcoinlib.core.b2lx(current_cblock.hashPrevBlock) # DB parent hash. blocks = list(cursor.execute('''SELECT * FROM blocks WHERE block_index = ?''', (current_index - 1,))) if len(blocks) != 1: # For empty DB. break db_parent = blocks[0]['block_hash'] # Compare. assert type(db_parent) == str assert type(backend_parent) == str if db_parent == backend_parent: break else: current_index -= 1 requires_rollback = True # Rollback for reorganisation. if requires_rollback: # Record reorganisation. logger.warning('Blockchain reorganisation at block {}.'.format(current_indexM log.message(db, block_index, 'reorg', None, {'block_index': current_index}) # Rollback the DB. reparse(db, block_index=current_index-1, quiet=True) block_index = current_index tx_index = get_next_tx_index(db) continue # Check version. (Don t add any blocks to the database while # running an out check.software_version() Get and parse transactions in this block (atomically). block_hash = backend.getblockhash(current_index) block = backend.getblock(block_hash) previous_block_hash = bitcoinlib.core.b2lx(block.hashPrevBlock) block_time = block.nTime txhash_list, raw_transactions = backend.get_tx_list(block) with db: util.CURRENT_BLOCK_INDEX = block_index # List the block. cursor.execute('''INSERT INTO blocks( block_index, block_hash, block_time, previous_block_hash, difficulty) VALUES(?,?,?,?,?)''', (block_index, block_hash, block_time, previous_block_hash, block.difficuM ) # List the transactions in the block. for tx_hash in txhash_list: tx_hex = raw_transactions[tx_hash] tx_index = list_tx(db, block_hash, block_index, block_time, tx_hash, tx_index, tx_hex) # Parse the transactions in the block. new_ledger_hash, new_txlist_hash, new_messages_hash, found_messages_hash = parse_block(db, block_index, block_time) # When newly caught uM p, check for conservation of assets. if block_index == block_count: if config.CHECK_ASSET_CONSERVATION: check.asset_conservation(db) # Remove any non supported transactions older than ten blocks. while len(not_supported_sorted) and not_supported_sorted[0][0] <= block_index - 10: tx_h = not_supported_sorted.popleft()[1] del not_supported[tx_h] logger.info('Block: %s (%ss, hashes: L:%s / TX:%s / M:%M str(block_index), "{:.2f}".format(time.time() - start_time, 3), new_ledger_hash[-5:], new_txlist_hash[-5:], new_messages_hash[-5:], (' [overwrote %s]' % found_messages_hash) if found_messages_hash and found_messages_hash != new_messages_hash else '')) # Increment block index. block_count = backend.getblockcount() block_index += 1 # TODO: add zeromq support here to await TXs and Blocks instead ofM # Get old mempool. old_mempool = list(cursor.execute('''SELECT * FROM mempool''')) old_mempool_hashes = [message['tx_hash'] for message in old_mempool] if backend.MEMPOOL_CACHE_INITIALIZED is False: backend.init_mempool_cache() logger.info("Ready for queries.") # Fake values for fake block. curr_time = int(time.time()) mempool_tx_index = tx_index xcp_mempool = [] raw_mempool = backend.getrawmempool() # this is a quick fix to make counterparty usable on high mempool situations # however, this makes the mempool unreliable on counterparty, a better, larger # fix must be done by changing this whole function into a zmq driven loop if len(raw_mempool) > config.MEMPOOL_TXCOUNT_UPDATE_LIMIT: continue # For each transaction in Bitcoin Core mempool, if it # a fake block,M a fake transaction, capture the generated messages, # and then save those messages. # Every transaction in mempool is parsed independently. (DB is rolled back after each one.) # We first filter out which transactions we've already parsed before so we can batch fetch their raw data parse_txs = [] for tx_hash in raw_mempool: # If already in mempool, copy to new one. if tx_hash in old_mempool_hashes: for meM ssage in old_mempool: if message['tx_hash'] == tx_hash: xcp_mempool.append((tx_hash, message)) # If not a supported XCP transaction, skip. elif tx_hash in not_supported: pass # Else: list, parse and save it. else: parse_txs.append(tx_hash) # fetch raw for all transactions that need to be parsed # Sometimes the transactions can ound: `{'code': -5, 'message': 'No information available about transaction'}` # - is txindex enabled in Bitcoind? # - or was there a block found while batch feting the raw txs # - or was there a double spend for w/e reason accepted into the mempool (replace-by-fee?) raw_transactions = backend.getrawtransaction_batch(parse_txs) except Exception as e: logger.warning('Failed to fetch raw for mempool TXs, restarting loopM continue # restart the follow loop for tx_hash in parse_txs: try: with db: # List the fake block. cursor.execute('''INSERT INTO blocks( block_index, block_hash, block_time) VALUES(?,?,?)''', (config.MEMPOOL_BLOCK_INDEX, config.MEMPOOL_BLOCK_HASH, curr_time) ) tx_hex = raw_transactions[tx_hash] if tx_hex is None: logger.debug('tx_hash %s not found in backend. Not adding to mempool.', (tx_hash, )) raise MempoolError mempool_tx_index = list_tx(db, None, block_index, curr_time, tx_hash, tx_index=M mempool_tx_index, tx_hex=tx_hex) # Parse transaction. cursor.execute('''SELECT * FROM transactions WHERE tx_hash = ?''', (tx_hash,)) transactions = list(cursor) if transactions: assert len(transactions) == 1 transaction = transactions[0] supported = parse_tx(db, transaction) if not supported: not_supported[tx_hash] = '' not_supported_sorted.append((block_index, tx_hash)) else: # If a transaction hasn # table `transactions`, then it # Counterparty transaction. not_supported[tx_hash] = '' not_supported_sorted.append((block_index, tx_hash)) M # Save transaction and side cursor.execute('''SELECT * FROM messages WHERE block_index = ?''', (config.MEMPOOL_BLOCK_INDEX,)) for message in list(cursor): xcp_mempool.append((tx_hash, message)) # Rollback. raise MempoolError except exceptions.ParseTransactionError as e: logger.warn('ParseTranM sactionError for tx %s: %s' % (tx_hash, e)) except MempoolError: pass write mempool messages to database. with db: cursor.execute('''DELETE FROM mempool''') for message in xcp_mempool: tx_hash, new_message = message new_message['tx_hash'] = tx_hash cursor.execute('''INSERT INTO mempool VALUES(:tx_hash, :command, :category, :bindings, :timestamp)''', new_messM elapsed_time = time.time() - start_time sleep_time = config.BACKEND_POLL_INTERVAL - elapsed_time if elapsed_time <= config.BACKEND_POLL_INTERVAL else 0 logger.getChild('mempool').debug('Refresh mempool: %s XCP txs seen, out of %s total entries (took %ss, next refresh in %ss)' % ( len(xcp_mempool), len(raw_mempool), "{:.2f}".format(elapsed_time, 3), "{:.2f}".format(sleep_time, 3))) db.wal_checkpM oint(mode=apsw.SQLITE_CHECKPOINT_PASSIVE) time.sleep(sleep_time) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 logger = logging.getLogger(__name__) from counterpartylib.lib import config from counterpartylib.lib import util from counterpartylib.lib import exceptions from counterpartylib.lib import backend from counterpartylib.lib import database CONSENSUS_HASH_SEED = M 'We can only see a short distance ahead, but we can see plenty there that needs to be done.' CONSENSUS_HASH_VERSION_MAINNET = 2 CHECKPOINTS_MAINNET = { config.BLOCK_FIRST_MAINNET: {'ledger_hash': '766ff0a9039521e3628a79fa669477ade241fc4c0ae541c3eae97f34b547b0b7', 'txlist_hash': '766ff0a9039521e3628a79fa669477ade241fc4c0ae541c3eae97f34b547b0b7'}, 280000: {'ledger_hash': '265719e2770d5a6994f6fe49839069183cd842ee14f56c2b870e56641e8a8725', 'txlist_hash': 'a59b33b4633649db4f14586af47e258ed9b8884dbb7aa308fb1f49aM 290000: {'ledger_hash': '4612ed7034474b4ff1727eb0e216d533ebe7ac755fb015e0f9a170c063f3e84c', 'txlist_hash': 'c15423c849fd360d38cbd6c6c3ea37a07fece723da92353f3056facc2676d9e7'}, 300000: {'ledger_hash': '9a3dd4949780404d61e5ca1929f94a43f08eb0fa19ccb4b5d6a61cafd7943199', 'txlist_hash': 'efa02dbdcc4158a598e3b476ece5ba9cc8d26f3abc8ac3777ac6dde0f0afc7e6'}, 310000: {'ledger_hash': '45e43d5cc77ea01129df01d7f55b0c89b2d4e18cd3d626fd92f30bfb37a85f4d', 'txlist_hash': '83cdcf75833d828ded09979b601fde87e2fM db0f5eb1cc6ab5d2042b7ec85f90e'}, 320000: {'ledger_hash': '91c1d33626669e8098bc762b1a9e3f616884e4d1cadda4881062c92b0d3d3e98', 'txlist_hash': '761793042d8e7c80e14a16c15bb9d40e237c468a87c207a59730b616bdfde7d4'}, 330000: {'ledger_hash': 'dd56aa97e5ca15841407f383ce1d7814536a594d7cfffcb4cf60bee8b362065a', 'txlist_hash': '3c45b4377a99e020550a198daa45c378c488a72ba199b53deb90b320d55a897b'}, 334000: {'ledger_hash': '24c4fa4097106031267439eb9fbe8ce2a18560169c67726652b608908c1ca9bb', 'txlist_hash': '764ca9e8d3b9546M d1c4ff441a39594548989f60daefc6f28e046996e76a273bf'}, 335000: {'ledger_hash': 'e57c9d606a615e7e09bf99148596dd28e64b25cd8b081e226d535a64c1ed08d1', 'txlist_hash': '437d9507185b5e193627edf4998aad2264755af8d13dd3948ce119b32dd50ce2'}, 336000: {'ledger_hash': '1329ff5b80d034b64f6ea3481b7c7176437a8837b2a7cb7b8a265fdd1397572d', 'txlist_hash': '33eb8cacd4c750f8132d81e8e43ca13bd565f1734d7d182346364847414da52f'}, 337000: {'ledger_hash': '607e6a93e8d97cefea9bd55384898ee90c8477ded8a46017f2294feedbc83409', 'txlist_hasM h': '20b535a55abcc902ca70c19dd648cbe5149af8b4a4157b94f41b71fc422d428e'}, 338000: {'ledger_hash': 'f043914c71e4b711abb1c1002767b9a4e7d605e249facaaf7a2046b0e9741204', 'txlist_hash': 'fa2c3f7f76345278271ed5ec391d582858e10b1f154d9b44e5a1f4896400ee46'}, 339000: {'ledger_hash': '49f7240bc90ebc2f242dd599c7d2c427b9d2ac844992131e6e862b638ae4393a', 'txlist_hash': 'c1e3b497c054dcf67ddd0dc223e8b8a6e09a1a05bacb9fef5c03e48bd01e64e7'}, 340000: {'ledger_hash': '255760e2abfb79fdd76b65759f1590f582c1747f3eeccc4b2ae37d23e3M 0e0729', 'txlist_hash': '8502004bb63e699b243ac8af072d704c69b817905e74787c2031af971e8cd87c'}, 341000: {'ledger_hash': '1369cba3909e564d2e725879a8b2cd987df075db121d1d421c8ce16b65f4bf04', 'txlist_hash': 'd217d0bed190cb27f58fcb96b255f8006bc4b9ed739e1bb08507201c49c426c8'}, 342000: {'ledger_hash': '9e7e9b8620717189ccea697ff2f84fe71bc4ae8d991481ff235164d72a9e6e4f', 'txlist_hash': 'adf75d023760101b2b337f6359dd811b12521c83837eb3f7db3bbfd0b095aa54'}, 343000: {'ledger_hash': 'aa47312ebe94b35504bec6c74713e404e5f368M 54e0836839344d13debe50558c', 'txlist_hash': '6bdbbc96364b3c92cea132fe66a0925f9445a249f7062326bdcc4ad4711f0c01'}, 344000: {'ledger_hash': '40187263aa96d1362bf7b19c8ba0fff7f0c0f3eb132a40fc90601b5926c7e6e3', 'txlist_hash': '98da8efe705c4b54275bfd25f816a7e7a4ff1f67647e17d7a0aaa2a3fef8bda0'}, 345000: {'ledger_hash': 'e4a1e1be4beea63d9740ca166b75bb4e3ffa2af33e1fe282e5b09c4952a7448c', 'txlist_hash': '777f163eaa5ad79dcb738871d4318a0699defec469d8afe91ab6277ff8d3e8b8'}, 350000: {'ledger_hash': '6a67e9f2e9d07e7bb3M 277cf9c24f84c857ed1b8fff4a37e589cd56ade276dd95', 'txlist_hash': '96bcbdbce74b782a845d4fda699846d2d3744044c2870a413c018642b8c7c3bf'}, 355000: {'ledger_hash': 'a84b17992217c7845e133a8597dac84eba1ee8c48bcc7f74bcf512837120f463', 'txlist_hash': '210d96b42644432b9e1a3433a29af9acb3bad212b67a7ae1dbc011a11b04bc24'}, 360000: {'ledger_hash': 'ddca07ea43b336b703fb8ebab6c0dc30582eb360d6f0eb0446e1fe58b53dee0a', 'txlist_hash': '31d0ff3e3782cf9464081829c5595b3de5ac477290dc069d98672f3f552767f8'}, 365000: {'ledger_hash':M '2d55b126cca3eca15c07b5da683988f9e01d7346d2ca430e940fd7c07ce84fd7', 'txlist_hash': '7988a823cc1e3234953cc87d261d3c1fede8493d0a31b103357eb23cc7dc2eda'}, 366000: {'ledger_hash': '64ce274df2784f9ca88a8d7071613ec6527e506ec31cd434eca64c6a3345a6b7', 'txlist_hash': '0d4374da6100e279b24f4ba4a2d6afbfc4fb0fc2d312330a515806e8c5f49404'}, 370000: {'ledger_hash': 'fabb2a2e91fad3fe7734169d554cca396c1030243044cef42fcf65717cf0fa61', 'txlist_hash': '41d1732868c9ac25951ace5ca9f311a15d5eca9bf8d548e0d988c050bd2aff87'}, 000: {'ledger_hash': 'a7ac4e2948cea0c426c8fc201cf57d9c313027ea7bff2b32a25ed28d3dbaa581', 'txlist_hash': '96118a7aa2ca753488755b7419a0f44a7fbc371bc58dcc7ab083c70fc14ef8b3'}, 380000: {'ledger_hash': '70453ba04c1c0198c4771e7964cffa25f9456c2f71456a8b05dfe935d5fcdc88', 'txlist_hash': '8bf2070103cca6f0bde507b7d20b0ba0630da6349beb560fa64c926d08dbcaef'}, 385000: {'ledger_hash': '93eb0a6e820bee197e7591edbc5ead7bfa38f32c88aabf4785f080fd6ae96c4c', 'txlist_hash': '1f8f17fd5766382a8c10a2a0e995a7d5a5d1bcd5fc0220d1e2691b2M 390000: {'ledger_hash': '7d42b98eecbc910a67a5f4ac8dc7d6d9b6995ebc5bdf53663b414965fe7d2c5e', 'txlist_hash': 'b50efc4a4241bf3ec33a38c3b5f34756a9f305fe5fa9a80f7f9b70d5d7b2a780'}, 395000: {'ledger_hash': '89f9ac390b35e69dd75d6c34854ba501dce2f662fc707aee63cad5822c7660f2', 'txlist_hash': '2151dd2f0aa14685f3d041727a689d5d242578072a049123b317724fc4f1100c'}, 400000: {'ledger_hash': 'eb681a305125e04b6f044b36045e23ee248ce4eb68433cea2b36d15e7e74d5f1', 'txlist_hash': 'b48e9501e8d6f1f1b4127d868860885d3dbM 76698c2c31a567777257df101cf61'}, 405000: {'ledger_hash': '3725055b37a8958ade6ca1c277cf50fee6036b4a92befb8da2f7c32f0b210881', 'txlist_hash': '871b2adfd246e3fe69f0fe9098e3251045ed6e9712c4cf90ea8dfdd1eb330ed6'}, 410000: {'ledger_hash': '1fa9a34f233695ebd7ebb08703bf8d99812fa099f297efc5d307d1ebef902ffd', 'txlist_hash': 'ee3bd84c728a37e2bbe061c1539c9ee6d71db18733b1ed53ee8d320481f55030'}, 415000: {'ledger_hash': '6772a8a1c784db14c0bf111e415919c9da4e5ca142be0b9e323c82c1b13c74e0', 'txlist_hash': 'cfb81785cd48e9bM a0e54fee4d62f49b347489da82139fd5e1555ae0bc11a33d5'}, 420000: {'ledger_hash': '42167117e16943f44bb8117aa0a39bed2d863a454cd694d0bc5006a7aab23b06', 'txlist_hash': 'a1139870bef8eb9bbe60856029a4f01fce5432eb7aeacd088ba2e033757b86e3'}, CONSENSUS_HASH_VERSION_TESTNET = 7 CHECKPOINTS_TESTNET = { config.BLOCK_FIRST_TESTNET: {'ledger_hash': '63f0fef31d02da85fa779e9a0e1b585b1a6a4e59e14564249e288e074e91c223', 'txlist_hash': '63f0fef31d02da85fa779e9a0e1b585b1a6a4e59e14564249e288e074e91c223'}, 316000: {'ledger_hashM ': 'f645e6877da416b8b91670ac927df686c5ea6fc1158c150ae49d594222ed504c', 'txlist_hash': '3e29bcbf3873326097024cc26e9296f0164f552dd79c2ee7cfc344e6d64fa87d'}, 319000: {'ledger_hash': '384ca28ac56976bc24a6ab7572b41bc61474e6b87fdee814135701d6a8f5c8a2', 'txlist_hash': '6c05c98418a6daa6de82dd59e000d3f3f5407c5432d4ab7d76047873a38e4d4b'}, 322000: {'ledger_hash': 'f4015c37eb4f31ac42083fd0389cde4868acb5353d3f3abfe2f3a88aba8cae72', 'txlist_hash': '18f278154e9bc3bbcc39da905ab4ad3023742ab7723b55b0fd1c58c36cd3e9bf'}, 25000: {'ledger_hash': 'd7f70a927f5aeed38e559ddc0bc4697601477ea43cde928ad228fefc195b02da', 'txlist_hash': '1a60e38664b39e0f501b3e5a60c6fc0bd4ed311b74872922c2dde4cb2267fd3e'}, 329000: {'ledger_hash': '96637b4400cbe084c2c4f139f59b5bc16770815e96306423aaeb2b2677a9a657', 'txlist_hash': '79d577d8fbba0ad6ae67829dfa5824f758286ccd429d65b7d1d42989134d5b57'}, 350000: {'ledger_hash': 'cae8fec787bba3d5c968a8f4b6fb22a54c96d5acbeadd0425f6b20c3a8813ea3', 'txlist_hash': '097df9c3079df4d96f59518df72492dfd7a79716462e3a4a30d62M 400000: {'ledger_hash': '94abfd9c00c8462c155f64011e71af141b7d524e17de5aeda26b7469fe79b5f0', 'txlist_hash': 'a9fc42b69f80ec69f3f98e8a3cd81f4f946544fd0561a62a0891254c16970a87'}, 450000: {'ledger_hash': '09eb9f2aa605ce77225362b4b556284acdd9f6d3bc273372dfae4a5be9e9b035', 'txlist_hash': '05af651c1de49d0728834991e50000fbf2286d7928961b71917f682a0f2b7171'}, 500000: {'ledger_hash': '85f3bca8c88246ddfa1a5ec327e71f0696c182ed2a5fedf3712cd2e87e2661ac', 'txlist_hash': '663b34955116a96501e0c1c27f27d24baM d7d45995913367553c5cfe4b8b9d0a9'}, 550000: {'ledger_hash': 'c143026133af2d83bc49ef205b4623194466ca3e7c79f95da2ad565359ccb5ad', 'txlist_hash': '097b8bca7a243e0b9bdf089f34de15bd2dcd4727fb4e88aae7bfd96302250326'}, 600000: {'ledger_hash': '82caf720967d0e43a1c49a6c75f255d9056ed1bffe3f96d962478faccdaba8ff', 'txlist_hash': '0d99f42184233426d70102d5ac3c80aaecf804d441a8a0d0ef26038d333ab7a7'}, 650000: {'ledger_hash': 'bef100ae7d5027a8b3f32416c4f26e1f16b21cee2a986c57be1466a3ba338051', 'txlist_hash': '409ed86e4274bM 511193d187df92e433c734dcc890bf93496e7a7dee770e7035e'}, 700000: {'ledger_hash': 'afe5e9c3f3a8c6f19c4f9feaf09df051c28202c6bae64f3563a09ffea9e79a6e', 'txlist_hash': '4f9765158855d24950c7e076615b0ad5b72738d4d579decfd3b93c998edf4fcb'}, 750000: {'ledger_hash': 'e7c7969a6156facb193b77ef71b5e3fac49c6998e5a94ec3b90292be10ece9cc', 'txlist_hash': '6e511790656d3ffec0c912d697e5d1c2a4e401a1606203c77ab5a5855891bc2c'}, 800000: {'ledger_hash': '42a7c679e51e5e8d38df26b67673b4850e8e6f72723aa19673b3219fcc02b77b', 'txlist_hM ash': '885ae1e6c21f5fb3645231aaa6bb6910fc21a0ae0ca5dbe9a4011f3b5295b3e7'}, 850000: {'ledger_hash': '35b2a2ab4a8bfbc321d4545292887b4ccaea73415c7674f795aefa6e240890eb', 'txlist_hash': '72d5cfe1e729a22da9eacd4d7752c881c43a191904556b65a0fae82b770dcdf3'}, 900000: {'ledger_hash': 'a5552b4998d2e5a516b9310d6592e7368771c1ad3b6e6330f6bc0baa3db31643', 'txlist_hash': '5a2e9fbd9b52ee32b8e8bfff993ed92dc22510aa7448277a704176cf01e55b04'}, 950000: {'ledger_hash': '5a5e78b55ac294690229abff7ff8f74f390f3a47dc4d08a0bac40e2eM 89a5bed2', 'txlist_hash': 'f4fa9838fb38d3e5beffb760fae022dcc59c61c506dd28ac83ee48ba814d04b2'}, 1000000: {'ledger_hash': 'eafca6700b9fd8f3992f8a18316e9ad59480ef74a4e7737793c101878aba8e1a', 'txlist_hash': '03deb626e031f30acd394bf49c35e11a487cb11e55dff5ba9a3f6d04b460c7de'}, 1050000: {'ledger_hash': '8012ebaf4c6638173e88ecd3e7bb2242ab88a9bdf877fc32c42dbcd7d2d3bab1', 'txlist_hash': '896274fdba957961083b07b80634126bc9f0434b67d723ed1fa83157ce5cd9a7'}, 1100000: {'ledger_hash': '76357f917235daa180c904cdf5c44366eM ef3e33539b7b0ba6a38f89582e82d22', 'txlist_hash': '36ecfd4b07f23176cd6960bc0adef97472c13793e53ac3df0eea0dd2e718a570'}, 1150000: {'ledger_hash': '5924f004bfdc3be449401c764808ebced542d2e06ba30c5984830292d1a926aa', 'txlist_hash': '9ff139dacf4b04293074e962153b972d25fa16d862dae05f7f3acc15e83c4fe8'}, 1200000: {'ledger_hash': 'a3d009bd2e0b838c185b8866233d7b4edaff87e5ec4cc4719578d1a8f9f8fe34', 'txlist_hash': '11dcf3a0ab714f05004a4e6c77fe425eb2a6427e4c98b7032412ab29363ffbb2'}, 1250000: {'ledger_hash': '37244453b4M eac67d1dbfc0f60116cac90dab7b814d756653ad3d9a072fbac61a', 'txlist_hash': 'c01ed3113f8fd3a6b54f5cefafd842ebf7c314ce82922e36236414d820c5277a'}, 1300000: {'ledger_hash': 'a83c1cd582604130fd46f1304560caf0f4e3300f3ce7c3a89824b8901f13027f', 'txlist_hash': '67e663b75a80940941b8370ada4985be583edaa7ba454d49db9a864a7bb7979c'}, 1350000: {'ledger_hash': 'f96e6aff578896a4568fb69f72aa0a8b52eb9ebffefca4bd7368790341cd821d', 'txlist_hash': '83e7d31217af274b13889bd8b9f8f61afcd7996c2c8913e9b53b1d575f54b7c1'}, dger_hash': '85a23f6fee9ce9c80fa335729312183ff014920bbf297095ac77c4105fb67e17', 'txlist_hash': 'eee762f34a3f82e6332c58e0c256757d97ca308719323af78bf5924f08463e12'}, CONSENSUS_HASH_VERSION_REGTEST = 1 CHECKPOINTS_REGTEST = { config.BLOCK_FIRST_REGTEST: {'ledger_hash': '33cf0669a0d309d7e6b1bf79494613b69262b58c0ea03c9c221d955eb4c84fe5', 'txlist_hash': '33cf0669a0d309d7e6b1bf79494613b69262b58c0ea03c9c221d955eb4c84fe5'}, class ConsensusError(Exception): def consensus_hash(db, field, previous_consensusM cursor = db.cursor() block_index = util.CURRENT_BLOCK_INDEX # Initialise previous hash on first block. if block_index <= config.BLOCK_FIRST: assert not previous_consensus_hash previous_consensus_hash = util.dhash_string(CONSENSUS_HASH_SEED) # Get previous hash. if not previous_consensus_hash: previous_consensus_hash = list(cursor.execute('''SELECT * FROM blocks WHERE block_index = ?''', (block_index - 1,)))[0][field] previous_consensus_hash = None if not previous_consensus_hash: raise ConsensusError('Empty previous {} for block {}. Please launch a `reparse`.'.format(field, block_index)) # Calculate current hash. if config.TESTNET: consensus_hash_version = CONSENSUS_HASH_VERSION_TESTNET elif config.REGTEST: consensus_hash_version = CONSENSUS_HASH_VERSION_REGTEST consensus_hash_version = CONSENSUS_HASH_VERSION_MAINNET calculated_hash =M util.dhash_string(previous_consensus_hash + '{}{}'.format(consensus_hash_version, ''.join(content))) # Verify hash (if already in database) or save hash (if not). # NOTE: do not enforce this for messages_hashes, those are more informational (for now at least) found_hash = list(cursor.execute('''SELECT * FROM blocks WHERE block_index = ?''', (block_index,)))[0][field] or None if found_hash and field != 'messages_hash': # Check against existing value. if calculated_hash != found_hashM raise ConsensusError('Inconsistent {} for block {} (calculated {}, vs {} in database).'.format( field, block_index, calculated_hash, found_hash)) # Save new hash. cursor.execute('''UPDATE blocks SET {} = ? WHERE block_index = ?'''.format(field), (calculated_hash, block_index)) # Check against checkpoints. if config.TESTNET: checkpoints = CHECKPOINTS_TESTNET elif config.REGTEST: checkpoints = CHECKPOINTS_REGTEST eckpoints = CHECKPOINTS_MAINNET if field != 'messages_hash' and block_index in checkpoints and checkpoints[block_index][field] != calculated_hash: raise ConsensusError('Incorrect {} hash for block {}. Calculated {} but expected {}'.format(field, block_index, calculated_hash, checkpoints[block_index][field],)) return calculated_hash, found_hash class SanityError(Exception): def asset_conservation(db): logger.debug('Checking for conservation of assets.') supplies = util.supplies(M held = util.held(db) for asset in supplies.keys(): asset_issued = supplies[asset] asset_held = held[asset] if asset in held and held[asset] != None else 0 if asset_issued != asset_held: raise SanityError('{} {} issued {} {} held'.format(util.value_out(db, asset_issued, asset), asset, util.value_out(db, asset_held, asset), asset)) logger.debug('{} has been conserved ({} {} both issued and held)'.format(asset, util.value_out(db, asset_issued, asset), asset))M class VersionError(Exception): class VersionUpdateRequiredError(VersionError): def check_change(protocol_change, change_name): # Check client version. if config.VERSION_MAJOR < protocol_change['minimum_version_major']: passed = False elif config.VERSION_MAJOR == protocol_change['minimum_version_major']: if config.VERSION_MINOR < protocol_change['minimum_version_minor']: passed = False elif config.VERSION_MINOR == protocol_changeM ['minimum_version_minor']: if config.VERSION_REVISION < protocol_change['minimum_version_revision']: passed = False explanation = 'Your version of {} is v{}, but, as of block {}, the minimum version is v{}.{}.{}. Reason: . Please upgrade to the latest version and restart the server.'.format( config.APP_NAME, config.VERSION_STRING, protocol_change['block_index'], protocol_change['minimum_version_major'], protocol_change['minimum_version_minoM protocol_change['minimum_version_revision'], change_name) if util.CURRENT_BLOCK_INDEX >= protocol_change['block_index']: raise VersionUpdateRequiredError(explanation) warnings.warn(explanation) def software_version(): if config.FORCE: logger.debug('Checking version.') host = 'https://counterpartyxcp.github.io/counterparty-lib/counterpartylib/protocol_changes.json' response = requests.get(host, headers={'M cache-control': 'no-cache'}) versions = json.loads(response.text) except (requests.exceptions.ConnectionError, ConnectionRefusedError, ValueError) as e: logger.warning('Unable to check version! ' + str(sys.exc_info()[1])) for change_name in versions: protocol_change = versions[change_name] check_change(protocol_change, change_name) except VersionUpdateRequiredError as e: logger.error("Version Update Required", exc_info=sys.M sys.exit(config.EXITCODE_UPDATE_REQUIRED) logger.debug('Version check passed.') class DatabaseVersionError(Exception): def __init__(self, message, reparse_block_index): super(DatabaseVersionError, self).__init__(message) self.reparse_block_index = reparse_block_index def database_version(db): if config.FORCE: logger.debug('Checking database version.') version_major, version_minor = database.version(db) if version_major != config.VERM # Rollback database if major version has changed. raise DatabaseVersionError('Client major version number mismatch ({} {}).'.format(version_major, config.VERSION_MAJOR), config.BLOCK_FIRST) elif version_minor != config.VERSION_MINOR: # Reparse all transactions if minor version has changed. raise DatabaseVersionError('Client minor version number mismatch ({} {}).'.format(version_minor, config.VERSION_MINOR), None) # vim: tabstop=8 expandtab shiftwidth=4 softtaM """Variables prefixed with `DEFAULT` should be able to be overridden by configuration file and command UNIT = 100000000 # The same across assets. VERSION_REVISION = 1 VERSION_STRING = str(VERSION_MAJOR) + '.' + str(VERSION_MINOR) + '.' + str(VERSION_REVISION) # Counterparty protocol TXTYPE_FORMAT = '>I' SHORT_TXTYPE_FORMAT = 'B' TWO_WEEKS = 2 * 7 * 24 * 3600 MAX_EXPIRATION = 4 * 2016 # TM MEMPOOL_BLOCK_HASH = 'mempool' MEMPOOL_BLOCK_INDEX = 9999999 OP_RETURN_MAX_SIZE = 80 # bytes # Currency agnosticism BTC_NAME = 'Bitcoin' XCP_NAME = 'Counterparty' APP_NAME = XCP_NAME.lower() DEFAULT_RPC_PORT_REGTEST = 24000 DEFAULT_RPC_PORT_TESTNET = 14000 DEFAULT_RPC_PORT = 4000 DEFAULT_BACKEND_PORT_REGTEST = 28332 DEFAULT_BACKEND_PORT_TESTNET = 18332 DEFAULT_BACKEND_PORT = 8332 DEFAULT_INDEXD_PORT_REGTEST = 28432 DEXD_PORT_TESTNET = 18432 DEFAULT_INDEXD_PORT = 8432 UNSPENDABLE_REGTEST = 'mvCounterpartyXXXXXXXXXXXXXXW24Hef' UNSPENDABLE_TESTNET = 'mvCounterpartyXXXXXXXXXXXXXXW24Hef' UNSPENDABLE_MAINNET = '1CounterpartyXXXXXXXXXXXXXXXUWLpVr' ADDRESSVERSION_TESTNET = b'\x6f' P2SH_ADDRESSVERSION_TESTNET = b'\xc4' PRIVATEKEY_VERSION_TESTNET = b'\xef' ADDRESSVERSION_MAINNET = b'\x00' P2SH_ADDRESSVERSION_MAINNET = b'\x05' PRIVATEKEY_VERSION_MAINNET = b'\x80' ADDRESSVERSION_REGTEST = b'\x6f' P2SH_ADDRESSVERSION_REGTEST = b'\xc4' RIVATEKEY_VERSION_REGTEST = b'\xef' MAGIC_BYTES_TESTNET = b'\xfa\xbf\xb5\xda' # For bip-0010 MAGIC_BYTES_MAINNET = b'\xf9\xbe\xb4\xd9' # For bip-0010 MAGIC_BYTES_REGTEST = b'\xda\xb5\xbf\xfa' BLOCK_FIRST_TESTNET_TESTCOIN = 310000 BURN_START_TESTNET_TESTCOIN = 310000 BURN_END_TESTNET_TESTCOIN = 4017708 # Fifty years, at ten minutes per block. BLOCK_FIRST_TESTNET = 310000 BLOCK_FIRST_TESTNET_HASH = '000000001f605ec6ee8d2c0d21bf3d3ded0a31ca837acc98893876213828989d' BURN_START_TESTNET = 310000 T = 4017708 # Fifty years, at ten minutes per block. BLOCK_FIRST_MAINNET_TESTCOIN = 278270 BURN_START_MAINNET_TESTCOIN = 278310 BURN_END_MAINNET_TESTCOIN = 2500000 # A long time. BLOCK_FIRST_MAINNET = 278270 BLOCK_FIRST_MAINNET_HASH = '00000000000000017bac9a8e85660ad348050c789922d5f8fe544d473368be1a' BURN_START_MAINNET = 278310 BURN_END_MAINNET = 283810 BLOCK_FIRST_REGTEST = 0 BLOCK_FIRST_REGTEST_HASH = '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206' BURN_START_REGTEST = 101 URN_END_REGTEST = 150000000 BLOCK_FIRST_REGTEST_TESTCOIN = 0 BURN_START_REGTEST_TESTCOIN = 101 BURN_END_REGTEST_TESTCOIN = 150 # NOTE: If the DUST_SIZE constants are changed, they MUST also be changed in counterblockd/lib/config.py as well DEFAULT_REGULAR_DUST_SIZE = 546 # TODO: Revisit when dust size is adjusted in bitcoin core DEFAULT_MULTISIG_DUST_SIZE = 7800 # <https://bitcointalk.org/index.php?topic=528023.msg7469941#msg7469941> DEFAULT_OP_RETURN_VALUE = 0 KB_ESTIMATE_SMART = 1024 DEFAULT_FEE_PER_KB = 25000 # sane/low default, also used as minimum when estimated fee is used ESTIMATE_FEE_PER_KB = True # when True will use `estimatesmartfee` from bitcoind instead of DEFAULT_FEE_PER_KB ESTIMATE_FEE_CONF_TARGET = 3 ESTIMATE_FEE_MODE = 'CONSERVATIVE' DEFAULT_FEE_FRACTION_REQUIRED = .009 # 0.90% DEFAULT_FEE_FRACTION_PROVIDED = .01 # 1.00% DEFAULT_REQUESTS_TIMEOUT = 20 # 20 seconds DEFAULT_RPC_BATCH_SIZE = 20 # A 1 MB M block can hold about 4200 transactions. EXITCODE_UPDATE_REQUIRED = 5 DEFAULT_CHECK_ASSET_CONSERVATION = True BACKEND_RAW_TRANSACTIONS_CACHE_SIZE = 20000 BACKEND_RPC_BATCH_NUM_WORKERS = 6 UNDOLOG_MAX_PAST_BLOCKS = 100 #the number of past blocks that we store undolog history DEFAULT_UTXO_LOCKS_MAX_ADDRESSES = 1000 DEFAULT_UTXO_LOCKS_MAX_AGE = 3.0 #in seconds ADDRESS_OPTION_REQUIRE_MEMO = 1 ADDRESS_OPTION_MAX_VALUE = ADDRESS_OPTION_REQUIRE_MEMO # Or list of all the address options API_LIMIT_ROWS = 1000 MEMPOOL_TXCOUNT_UPDATE_LIMIT=60000 # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 logger = logging.getLogger(__name__) from counterpartylib.lib import config from counterpartylib.lib import util from counterpartylib.lib import exceptions from counterpartylib.lib import log def rowtracer(cursor, sql): """Converts fetched SQL data intM for index, (name, type_) in enumerate(cursor.getdescription()): dictionary[name] = sql[index] return dictionary def exectracer(cursor, sql, bindings): # This means that all changes to database must use a very simple syntax. # TODO: Need sanity checks here. sql = sql.lower() if sql.startswith('create trigger') or sql.startswith('drop trigger'): #CREATE TRIGGER stmts may include an "insert" or "update" as part of them array = sql.split('(')[0].split(' ') command = array[0] if 'insert' in sql: category = array[2] elif 'update' in sql: category = array[1] #CREATE TABLE, etc db = cursor.getconnection() dictionary = {'command': command, 'category': category, 'bindings': bindings} 'blocks', 'transactions', 'balances', 'messages', 'mempool', 'assets', 'new_sends', 'new_issuances' # interim table fM skip_tables_block_messages = copy.copy(skip_tables) if command == 'update': # List message manually. skip_tables += ['orders', 'bets', 'rps', 'order_matches', 'bet_matches', 'rps_matches'] # Record alteration in database. if category not in skip_tables: log.message(db, bindings['block_index'], command, category, bindings) # Record alteration in computation of message feed hash for the block if category not in skip_tables_block_messages: # don't include asset_longname as part of the messages hash # until subassets are enabled if category == 'issuances' and not util.enabled('subassets'): if isinstance(bindings, dict) and 'asset_longname' in bindings: del bindings['asset_longname'] # don't include memo as part of the messages hash # until enhanced_sends are enabled if category == 'sends' and not util.enabled('enhanced_sends'): if isinstance(bindings, dict) and 'memo' in bindingsM : del bindings['memo'] sorted_bindings = sorted(bindings.items()) if isinstance(bindings, dict) else [bindings,] BLOCK_MESSAGES.append('{}{}{}'.format(command, category, sorted_bindings)) class DatabaseIntegrityError(exceptions.DatabaseError): def get_connection(read_only=True, foreign_keys=True, integrity_check=True): """Connects to the SQLite database, returning a db `Connection` object""" logger.debug('Creating connection to `{}`.'.format(config.DATABASE)) db = apsw.Connection(config.DATABASE, flags=apsw.SQLITE_OPEN_READONLY) db = apsw.Connection(config.DATABASE) cursor = db.cursor() # For integrity, security. if foreign_keys and not read_only: logger.info('Checking database foreign keys...') cursor.execute('''PRAGMA foreign_keys = ON''') cursor.execute('''PRAGMA defer_foreign_keys = ON''') rows = list(cursor.execute('''PRAGMA foreign_key_check''')) logger.debug('Foreign Key Error: {}'.format(row)) raise exceptions.DatabaseError('Foreign key check failed.') # So that writers don cursor.execute('''PRAGMA journal_mode = WAL''') logger.info('Foreign key check completed.') # Make case sensitive the `LIKE` operator. # For insensitive queries use 'UPPER(fieldname) LIKE value.upper()'' cursor.execute('''PRAGMA case_sensitive_like = ON''') if integrity_check: logger.info('Checking database integrity...') integral = False for i in range(10): # DUPE cursor.execute('''PRAGMA integrity_check''') rows = cursor.fetchall() if not (len(rows) == 1 and rows[0][0] == 'ok'): raise exceptions.DatabaseError('Integrity check failed.') integral = True break except DatabaseIntegrityError: time.sleep(1) continue if not integral: raise exceptions.DatabaseError('Could not perform integrity check.') logger.info('Integrity check completed.') db.setrowtrace(rowtracer) db.setexectrace(exectracer) cursor = db.cursor() user_version = cursor.execute('PRAGMA user_version').fetchall()[0]['user_version'] # manage old user_version if user_version == config.VERSION_MINOR: version_minor = user_version version_major = cM user_version = (config.VERSION_MAJOR * 1000) + version_minor cursor.execute('PRAGMA user_version = {}'.format(user_version)) version_minor = user_version % 1000 version_major = user_version // 1000 return version_major, version_minor def update_version(db): cursor = db.cursor() user_version = (config.VERSION_MAJOR * 1000) + config.VERSION_MINOR cursor.execute('PRAGMA user_version = {}'.format(user_version)) # Syntax?! logger.info('DaM tabase version number updated.') logger.info('Starting database VACUUM. This may take awhile...') cursor = db.cursor() cursor.execute('VACUUM') logger.info('Database VACUUM completed.') # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 class DatabaseError(Exception): class TransactionError(Exception): class ParseTransactionError(Exception): class AssetError (Exception): class AssetIDError(AssetError): class MessageError(Exception): class ComposeError(MessageError): class UnpackError(MessageError): class ValidateError(MessageError): class DecodeError(MessageError): class PushDataDecodeError(DecodeError): class BTCOnlyError(MessageError): def __init__(self, msg, decodedTx=None): super(BTCOnlyError, self).__init__(msg) self.decodedTx = decodedTx class BalanceError(ExceM class EncodingError(Exception): class OptionsError(Exception): # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 logger = logging.getLogger(__name__) from datetime import datetime from dateutil.tz import tzlocal from colorlog import ColoredFormatter from counterpartylib.lib import config from counterpartylib.lib import exceptions m counterpartylib.lib import util class ModuleLoggingFilter(logging.Filter): module level logging filter (NodeJS-style), ie: filters="*,-counterpartylib.lib,counterpartylib.lib.api" - counterpartycli.server - counterpartylib.lib.api but will not log: - counterpartylib.lib - counterpartylib.lib.backend.indexd def __init__(self, filters): self.filters = str(filters).split(",") self.catchall = "*" in selfM if self.catchall: self.filters.remove("*") def filter(self, record): Determine if specified record should be logged or not result = None for filter in self.filters: if filter[:1] == "-": if result is None and ModuleLoggingFilter.ismatch(record, filter[1:]): result = False if ModuleLoggingFilter.ismatch(record, filter): result = True if result is None: return self.catchall return result def ismatch(cls, record, name): Determine if the specified record matches the name, in the same way as original logging.Filter does, ie: 'counterpartylib.lib' will match 'counterpartylib.lib.check' nlen = len(name) if nlen == 0: return True elif name == record.name: return True elif record.name.find(name, 0, nlenM return False return record.name[nlen] == "." def set_logger(logger): global ROOT_LOGGER if ROOT_LOGGER is None: ROOT_LOGGER = logger LOGGING_SETUP = False LOGGING_TOFILE_SETUP = False def set_up(logger, verbose=False, logfile=None, console_logfilter=None): global LOGGING_SETUP global LOGGING_TOFILE_SETUP def set_up_file_logging(): assert logfile max_log_size = 20 * 1024 * 1024 # 20 MB if os.name == 'nt': from counterpartylib.lib import util_windows fileh = util_windows.SanitizedRotatingFileHandler(logfile, maxBytes=max_log_size, backupCount=5) fileh = logging.handlers.RotatingFileHandler(logfile, maxBytes=max_log_size, backupCount=5) fileh.setLevel(logging.DEBUG) LOGFORMAT = '%(asctime)s [%(levelname)s] %(message)s' formatter = logging.Formatter(LOGFORMAT, '%Y-%m-%d-T%H:%M:%S%z') fileh.setFormatter(formatter) logger.addHandler(fileh) if LOGGING_SETUP: if logfile and not LOGGING_TOFILE_SETUP: set_up_file_logging() LOGGING_TOFILE_SETUP = True logger.getChild('log.set_up').debug('logging already setup') LOGGING_SETUP = True log_level = logging.DEBUG if verbose else logging.INFO logger.setLevel(log_level) # Console Logging console = logging.StreamHandler() console.setLevel(log_level) # only add [%(name)s] to LOGFORMAT if we're using console_logfilter GFORMAT = '%(log_color)s[%(asctime)s][%(levelname)s]' + ('' if console_logfilter is None else '[%(name)s]') + ' %(message)s%(reset)s' LOGCOLORS = {'WARNING': 'yellow', 'ERROR': 'red', 'CRITICAL': 'red'} formatter = ColoredFormatter(LOGFORMAT, "%Y-%m-%d %H:%M:%S", log_colors=LOGCOLORS) console.setFormatter(formatter) logger.addHandler(console) if console_logfilter: console.addFilter(ModuleLoggingFilter(console_logfilter)) set_up_file_logging() LOGGING_TOFILE_SETUP = True # Quieten noisy libraries. requests_log = logging.getLogger("requests") requests_log.setLevel(log_level) requests_log.propagate = False urllib3_log = logging.getLogger('urllib3') urllib3_log.setLevel(log_level) urllib3_log.propagate = False # Disable InsecureRequestWarning requests.packages.urllib3.disable_warnings() return int(time.time()) def isodt (epoch_time): return datetime.froM mtimestamp(epoch_time, tzlocal()).isoformat() return '<datetime>' def message(db, block_index, command, category, bindings, tx_hash=None): cursor = db.cursor() # Get last message index. messages = list(cursor.execute('''SELECT * FROM messages WHERE message_index = (SELECT MAX(message_index) from messages)''')) assert len(messages) == 1 message_index = messages[0]['message_index'] + 1 # Not to be misleading if block_index == config.MEMPOOL_BLOCK_INDEX: del bindings['status'] del bindings['block_index'] del bindings['tx_index'] except KeyError: # Handle binary data. for item in sorted(bindings.items()): if type(item[1]) == bytes: items.append((item[0], binascii.hexlify(item[1]).decode('ascii'))) items.append(item) _string = json.dumps(collections.OrderedDict(items)) cursor.execute('insert into messages values(:message_index, :block_index, :command, :category, :bindings, :timestamp)', (message_index, block_index, command, category, bindings_string, curr_time())) # Log only real transactions. if block_index != config.MEMPOOL_BLOCK_INDEX: log(db, command, category, bindings) def log (db, command, category, bindings): cursor = db.cursor() for element in bindM str(bindings[element]) except KeyError: bindings[element] = '<Error>' def output (quantity, asset): if asset not in ('fraction', 'leverage'): return str(util.value_out(db, quantity, asset)) + ' ' + asset return str(util.value_out(db, quantity, asset)) except exceptions.AssetError: return '<AssetError>' except decimal.DivisionByZero: return '<DivisionByZero>' except TypeError: return '<None>' if command == 'update': if category == 'order': logger.debug('Database: set status of order {} to {}.'.format(bindings['tx_hash'], bindings['status'])) elif category == 'bet': logger.debug('Database: set status of bet {} to {}.'.format(bindings['tx_hash'], bindings['status'])) elif category == 'order_matches': logger.debug('Database: set status of order_match {} to {M }.'.format(bindings['order_match_id'], bindings['status'])) elif category == 'bet_matches': logger.debug('Database: set status of bet_match {} to {}.'.format(bindings['bet_match_id'], bindings['status'])) elif category == 'dispensers': escrow_quantity = '' divisible = get_asset_info(cursor, bindings['asset'])['divisible'] if divisible: if "escrow_quantity" in bindings: escrow_quantity = "{:.8f}".format(bM indings["escrow_quantity"]/config.UNIT) if ("action" in bindings) and bindings["action"] == 'refill dispenser': logger.info("Dispenser: {} refilled a dispenser with {} {}".format(bindings["source"],escrow_quantity,bindings["asset"])) elif "prev_status" in bindings: #There was a dispense if bindings["prev_status"] == 0: if bindings["status"] == 10: logger.info("Dispenser: {} closed dispenser M for {} (dispenser empty)".format(bindings["source"],bindings["asset"])) elif bindings["status"] == 10: #Address closed the dispenser logger.info("Dispenser: {} closed dispenser for {} (operator closed)".format(bindings["source"],bindings["asset"])) # TODO: elif category == 'balances': # logger.debug('Database: set balance of {} in {} to {}.'.format(bindings['address'], bindings['asset'], output(bindings['quantity'], bindings['asset']).split(' ')[0])) if category == 'credits': logger.debug('Credit: {} to {} #{}# <{}>'.format(output(bindings['quantity'], bindings['asset']), bindings['address'], bindings['action'], bindings['event'])) elif category == 'debits': logger.debug('Debit: {} from {} #{}# <{}>'.format(output(bindings['quantity'], bindings['asset']), bindings['address'], bindings['action'], bindings['event'])) elif category == 'sends': logger.info('Send: {} from {} to {} ({}) [{}]M '.format(output(bindings['quantity'], bindings['asset']), bindings['source'], bindings['destination'], bindings['tx_hash'], bindings['status'])) elif category == 'orders': logger.info('Order: {} ordered {} for {} in {} blocks, with a provided fee of {:.8f} {} and a required fee of {:.8f} {} ({}) [{}]'.format(bindings['source'], output(bindings['give_quantity'], bindings['give_asset']), output(bindings['get_quantity'], bindings['get_asset']), bindings['expiration'], bindings['fee_provided'] / coM nfig.UNIT, config.BTC, bindings['fee_required'] / config.UNIT, config.BTC, bindings['tx_hash'], bindings['status'])) elif category == 'order_matches': logger.info('Order Match: {} for {} ({}) [{}]'.format(output(bindings['forward_quantity'], bindings['forward_asset']), output(bindings['backward_quantity'], bindings['backward_asset']), bindings['id'], bindings['status'])) elif category == 'btcpays': logger.info('{} Payment: {} paid {} to {} for order match {} ({}) [{}]'.formM at(config.BTC, bindings['source'], output(bindings['btc_amount'], config.BTC), bindings['destination'], bindings['order_match_id'], bindings['tx_hash'], bindings['status'])) elif category == 'issuances': if (get_asset_issuances_quantity(cursor, bindings["asset"]) == 0) or (bindings['quantity'] > 0): #This is the first issuance or the creation of more supply, so we have to log the creation of the token if bindings['divisible']: divisibility = 'divisible' unit = config.UNIT else: divisibility = 'indivisible' unit = 1 try: quantity = util.value_out(cursor, bindings['quantity'], None, divisible=bindings['divisible']) except Exception as e: quantity = '?' if 'asset_longname' in bindings and bindings['asset_longname'] is not None: logger.info('Subasset Issuance: {} created {} of {}M subasset {} as numeric asset {} ({}) [{}]'.format(bindings['source'], quantity, divisibility, bindings['asset_longname'], bindings['asset'], bindings['tx_hash'], bindings['status'])) else: logger.info('Issuance: {} created {} of {} asset {} ({}) [{}]'.format(bindings['source'], quantity, divisibility, bindings['asset'], bindings['tx_hash'], bindings['status'])) if bindings['locked']: lock_issuance = get_lock_issuance(cursor, bindings["assM if (lock_issuance == None) or (lock_issuance['tx_hash'] == bindings['tx_hash']): logger.info('Issuance: {} locked asset {} ({}) [{}]'.format(bindings['source'], bindings['asset'], bindings['tx_hash'], bindings['status'])) if bindings['transfer']: logger.info('Issuance: {} transfered asset {} to {} ({}) [{}]'.format(bindings['source'], bindings['asset'], bindings['issuer'], bindings['tx_hash'], bindings['status'])) elif category == 'broadcasts': if bindings['locked']: logger.info('Broadcast: {} locked his feed ({}) [{}]'.format(bindings['source'], bindings['tx_hash'], bindings['status'])) logger.info('Broadcast: ' + bindings['source'] + ' at ' + isodt(bindings['timestamp']) + ' with a fee of {}%'.format(output(D(bindings['fee_fraction_int'] / 1e8), 'fraction')) + ' (' + bindings['tx_hash'] + ')' + ' [{}]'.format(bindings['status'])) category == 'bets': logger.info('Bet: {} against {}, by {}, on {}'.format(output(bindings['wager_quantity'], config.XCP), output(bindings['counterwager_quantity'], config.XCP), bindings['source'], bindings['feed_address'])) elif category == 'bet_matches': placeholder = '' if bindings['target_value'] >= 0: # Only non negative values are valid. placeholder = ' that ' + str(output(bindings['target_value'], 'value')) if bindings['leverage']:M placeholder += ', leveraged {}x'.format(output(bindings['leverage'] / 5040, 'leverage')) logger.info('Bet Match: {} for {} against {} for {} on {} at {}{} ({}) [{}]'.format(util.BET_TYPE_NAME[bindings['tx0_bet_type']], output(bindings['forward_quantity'], config.XCP), util.BET_TYPE_NAME[bindings['tx1_bet_type']], output(bindings['backward_quantity'], config.XCP), bindings['feed_address'], isodt(bindings['deadline']), placeholder, bindings['id'], bindings['status'])) elif categoM logger.info('Dividend: {} paid {} per unit of {} ({}) [{}]'.format(bindings['source'], output(bindings['quantity_per_unit'], bindings['dividend_asset']), bindings['asset'], bindings['tx_hash'], bindings['status'])) elif category == 'burns': logger.info('Burn: {} burned {} for {} ({}) [{}]'.format(bindings['source'], output(bindings['burned'], config.BTC), output(bindings['earned'], config.XCP), bindings['tx_hash'], bindings['status'])) elif category == 'cM logger.info('Cancel: {} ({}) [{}]'.format(bindings['offer_hash'], bindings['tx_hash'], bindings['status'])) elif category == 'rps': log_message = 'RPS: {} opens game with {} possible moves and a wager of {}'.format(bindings['source'], bindings['possible_moves'], output(bindings['wager'], 'XCP')) logger.info(log_message) elif category == 'rps_matches': log_message = 'RPS Match: {} is playing a {}-moves game with {} with a wager of {} ({}) [{}M ]'.format(bindings['tx0_address'], bindings['possible_moves'], bindings['tx1_address'], output(bindings['wager'], 'XCP'), bindings['id'], bindings['status']) logger.info(log_message) elif category == 'rpsresolves': if bindings['status'] == 'valid': rps_matches = list(cursor.execute('''SELECT * FROM rps_matches WHERE id = ?''', (bindings['rps_match_id'],))) assert len(rps_matches) == 1 rps_match = rps_matches[0] log_mesM sage = 'RPS Resolved: {} is playing {} on a {}-moves game with {} with a wager of {} ({}) [{}]'.format(rps_match['tx0_address'], bindings['move'], rps_match['possible_moves'], rps_match['tx1_address'], output(rps_match['wager'], 'XCP'), rps_match['id'], rps_match['status']) log_message = 'RPS Resolved: {} [{}]'.format(bindings['tx_hash'], bindings['status']) logger.info(log_message) elif category == 'order_expirations': logger.info('Expired order: {M }'.format(bindings['order_hash'])) elif category == 'order_match_expirations': logger.info('Expired Order Match awaiting payment: {}'.format(bindings['order_match_id'])) elif category == 'bet_expirations': logger.info('Expired bet: {}'.format(bindings['bet_hash'])) elif category == 'bet_match_expirations': logger.info('Expired Bet Match: {}'.format(bindings['bet_match_id'])) elif category == 'bet_match_resolutions': cfd_type_id = util.BET_TYPE_ID['BullCFD'] + util.BET_TYPE_ID['BearCFD'] equal_type_id = util.BET_TYPE_ID['Equal'] + util.BET_TYPE_ID['NotEqual'] if bindings['bet_match_type_id'] == cfd_type_id: if bindings['settled']: logger.info('Bet Match Settled: {} credited to the bull, {} credited to the bear, and {} credited to the feed address ({})'.format(output(bindings['bull_credit'], config.XCP), output(bindings['bear_credit'], config.XCP), output(bindings['M fee'], config.XCP), bindings['bet_match_id'])) else: logger.info('Bet Match Force Liquidated: {} credited to the bull, {} credited to the bear, and {} credited to the feed address ({})'.format(output(bindings['bull_credit'], config.XCP), output(bindings['bear_credit'], config.XCP), output(bindings['fee'], config.XCP), bindings['bet_match_id'])) elif bindings['bet_match_type_id'] == equal_type_id: logger.info('Bet Match Settled: {} won the pot of {};M {} credited to the feed address ({})'.format(bindings['winner'], output(bindings['escrow_less_fee'], config.XCP), output(bindings['fee'], config.XCP), bindings['bet_match_id'])) elif category == 'rps_expirations': logger.info('Expired RPS: {}'.format(bindings['rps_hash'])) elif category == 'rps_match_expirations': logger.info('Expired RPS Match: {}'.format(bindings['rps_match_id'])) elif category == 'destructions': asset_info = get_asset_info(cursor, bM quantity = bindings['quantity'] if asset_info['divisible']: quantity = "{:.8f}".format(quantity/config.UNIT) logger.info('Destruction: {} destroyed {} {} with tag ({}) [{}]'.format(bindings['source'], quantity, bindings['asset'], bindings['tag'], bindings['tx_hash'], bindings['status'])) elif category == 'dispensers': each_price = bindings['satoshirate'] currency = config.BTC dispenser_label =M escrow_quantity = bindings['escrow_quantity'] give_quantity = bindings['give_quantity'] if (bindings['oracle_address'] != None) and util.enabled('oracle_dispensers'): each_price = "{:.2f}".format(each_price/100.0) oracle_last_price, oracle_fee, currency, oracle_last_updated = util.get_oracle_last_price(db, bindings['oracle_address'], bindings['block_index']) dispenser_label = 'oracle dispenser using {}'.forM mat(bindings['oracle_address']) each_price = "{:.8f}".format(each_price/config.UNIT) divisible = get_asset_info(cursor, bindings['asset'])['divisible'] if divisible: escrow_quantity = "{:.8f}".format(escrow_quantity/config.UNIT) give_quantity = "{:.8f}".format(give_quantity/config.UNIT) if bindings['status'] == 0: logger.info('Dispenser: {} opened a {} for aM sset {} with {} balance, giving {} {} for each {} {}'.format(bindings['source'], dispenser_label, bindings['asset'], escrow_quantity, give_quantity, bindings['asset'], each_price, currency)) elif bindings['status'] == 1: logger.info('Dispenser: {} (empty address) opened a {} for asset {} with {} balance, giving {} {} for each {} {}'.format(bindings['source'], dispenser_label, bindings['asset'], escrow_quantity, give_quantity, bindings['asset'], each_price, currency)) elif binM dings['status'] == 10: logger.info('Dispenser: {} closed a {} for asset {}'.format(bindings['source'], dispenser_label, bindings['asset'])) elif category == 'dispenses': cursor.execute('SELECT * FROM dispensers WHERE tx_hash=:tx_hash', { 'tx_hash': bindings['dispenser_tx_hash'] dispensers = cursor.fetchall() dispenser = dispensers[0] if (dispenser["oracle_address"] != None) and util.enabled('oracle_dispeM tx_btc_amount = get_tx_info(cursor, bindings['tx_hash'])/config.UNIT oracle_last_price, oracle_fee, oracle_fiat_label, oracle_last_price_updated = util.get_oracle_last_price(db, dispenser["oracle_address"], bindings['block_index']) fiatpaid = round(tx_btc_amount*oracle_last_price,2) logger.info('Dispense: {} from {} to {} for {:.8f} {} ({} {}) ({})'.format(output(bindings['dispense_quantity'], bindings['asset']), bindings['souM rce'], bindings['destination'], tx_btc_amount, config.BTC, fiatpaid, oracle_fiat_label, bindings['tx_hash'])) logger.info('Dispense: {} from {} to {} ({})'.format(output(bindings['dispense_quantity'], bindings['asset']), bindings['source'], bindings['destination'], bindings['tx_hash'])) def get_lock_issuance(cursor, asset): cursor.execute('''SELECT * FROM issuances \ WHERE (status = ? AND asset = ? AND locked = ?) ORDER BY tx_index ASC''', (M 'valid', asset, True)) issuances = cursor.fetchall() if len(issuances) > 0: return issuances[0] def get_asset_issuances_quantity(cursor, asset): cursor.execute('''SELECT COUNT(*) AS issuances_count FROM issuances \ WHERE (status = ? AND asset = ?) ORDER BY tx_index DESC''', ('valid', asset)) issuances = cursor.fetchall() return issuances[0]['issuances_count'] def get_asset_info(cursor, asset): if asset == config.BTC or asset == config.XCPM return {'divisible':True} cursor.execute('''SELECT * FROM issuances \ WHERE (status = ? AND asset = ?) ORDER BY tx_index DESC''', ('valid', asset)) issuances = cursor.fetchall() return issuances[0] def get_tx_info(cursor, tx_hash): cursor.execute('SELECT * FROM transactions WHERE tx_hash=:tx_hash', { 'tx_hash': tx_hash transactions = cursor.fetchall() transaction = transactions[0] return transaction["btc_amount"] # vim: tabstop=8 expanM dtab shiftwidth=4 softtabstop=4 #### message_type.py logger = logging.getLogger(__name__) from counterpartylib.lib import config from counterpartylib.lib import util def pack(message_type_id, block_index=None): # pack message ID into 1 byte if not zero if util.enabled('short_tx_type_id', block_index) and message_type_id > 0 and message_type_id < 256: return struct.pack(config.SHORT_TXTYPE_FORMAT, message_type_id) # pack into 4 bytes truct.pack(config.TXTYPE_FORMAT, message_type_id) # retuns both the message type id and the remainder of the message data def unpack(packed_data, block_index=None): message_type_id = None message_remainder = None if len(packed_data) > 1: # try to read 1 byte first if util.enabled('short_tx_type_id', block_index): message_type_id = struct.unpack(config.SHORT_TXTYPE_FORMAT, packed_data[:1])[0] if message_type_id > 0: message_remainder = packed_dataM return (message_type_id, message_remainder) # First message byte was 0. We will read 4 bytes if len(packed_data) > 4: message_type_id = struct.unpack(config.TXTYPE_FORMAT, packed_data[:4])[0] message_remainder = packed_data[4:] return (message_type_id, message_remainder) None of the functions/objects in this module need be passed `db`. Naming convention: a `pub` is either a pubkey or a pubkeyhash from bitcoin.core.key import CPubKey from bitcoin.bech32 import CBech32Data from counterpartylib.lib import util from counterpartylib.lib import config from counterpartylib.lib import exceptions b58_digits = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz' class InputError (Exception): class AddressError(Exception): class MultiSigAddressError(AddressError): class VersionByteError (AddressError): class Base58Error (AddressError): class Base58ChecksumError (Base58Error): def validate(address, allow_p2sh=True): """Make sure the address is valid. May throw `AddressError`. # Get array of pubkeyhashes to check. if is_multisig(address): pubkeyhashes = pubkeyhash_array(address) pubkeyhashes = [address] # Check validity by attempting to decode. for pubkeyhash in pubkeyhashes: if util.enabled('segwit_support'): if not is_bech32(pubkeyhasM base58_check_decode(pubkeyhash, config.ADDRESSVERSION) base58_check_decode(pubkeyhash, config.ADDRESSVERSION) except VersionByteError as e: if not allow_p2sh: raise e base58_check_decode(pubkeyhash, config.P2SH_ADDRESSVERSION) except Base58Error as e: if not util.enabled('segwit_support') or not is_bech32(pubkeyhash): raise e def base58_encode(binary): """Encode the adM endian bytes to integer n = int('0x0' + util.hexlify(binary), 16) # Divide that integer into base58 n, r = divmod(n, 58) res.append(b58_digits[r]) res = ''.join(res[::-1]) def base58_check_encode(original, version): """Check if base58 encoding is valid.""" b = binascii.unhexlify(bytes(original, 'utf-8')) binary = d + util.dhash(d)[:4] res = base58_encode(binary) # Encode leading zeros as base58 zeros if c == czero: pad += 1 address = b58_digits[0] * pad + res if original != util.hexlify(base58_check_decode(address, version)): raise AddressError('encoded address does not decode properly') def base58_decode(s): # Convert the string to an integer if c not in b58_digits: Base58Error('Not a valid Base58 character: digit = b58_digits.index(c) # Convert the integer to bytes res = binascii.unhexlify(h.encode('utf8')) # Add padding back. for c in s[:-1]: if c == b58_digits[0]: pad += 1 k = b'\x00' * pad + res def base58_check_decode_parts(s): """Decode from base58 and return partM k = base58_decode(s) addrbyte, data, chk0 = k[0:1], k[1:-4], k[-4:] return addrbyte, data, chk0 def base58_check_decode(s, version): """Decode from base58 and return data part.""" addrbyte, data, chk0 = base58_check_decode_parts(s) if addrbyte != version: raise VersionByteError('incorrect version byte') chk1 = util.dhash(addrbyte + data)[:4] if chk0 != chk1: raise Base58ChecksumError('Checksum mismatch: 0x{} 0x{}'.format(util.hexlify(chk0), util.hexlM def is_multisig(address): """Check if the address is multi array = address.split('_') return len(array) > 1 def is_p2sh(address): if is_multisig(address): return False base58_check_decode(address, config.P2SH_ADDRESSVERSION) except (VersionByteError, Base58Error): return False def is_bech32(address): b32data = CBech32Data(address) def is_fully_valid(pubkey_bin): """Check if the public key is valid.""" cpubkey = CPubKey(pubkey_bin) return cpubkey.is_fullyvalid def make_canonical(address): """Return canonical version of the address.""" if is_multisig(address): signatures_required, pubkeyhashes, signatures_possible = extract_array(address) [base58_check_decode(pubkeyhash, config.ADDRESSVERSION) for pubkeyhash in pubkeyhashes] except Base58Error: raise MultiSigAddreM signature address must use PubKeyHashes, not public keys.') return construct_array(signatures_required, pubkeyhashes, signatures_possible) return address def test_array(signatures_required, pubs, signatures_possible): """Check if multi signature data is valid.""" signatures_required, signatures_possible = int(signatures_required), int(signatures_possible) except (ValueError, TypeError): raise MultiSigAddressError('Signature values not inM if signatures_required < 1 or signatures_required > 3: raise MultiSigAddressError('Invalid signatures_required.') if signatures_possible < 2 or signatures_possible > 3: raise MultiSigAddressError('Invalid signatures_possible.') for pubkey in pubs: if '_' in pubkey: raise MultiSigAddressError('Invalid characters in pubkeys/pubkeyhashes.') if signatures_possible != len(pubs): raise InputError('Incorrect number of pubkeys/pubkeyhashes in multi def construct_array(signatures_required, pubs, signatures_possible): """Create a multi signature address.""" test_array(signatures_required, pubs, signatures_possible) address = '_'.join([str(signatures_required)] + sorted(pubs) + [str(signatures_possible)]) def extract_array(address): """Extract data from multi signature address.""" assert is_multisig(address) array = address.split('_') signatures_required, pubs, signatures_possible = array[0],M sorted(array[1:-1]), array[-1] test_array(signatures_required, pubs, signatures_possible) return int(signatures_required), pubs, int(signatures_possible) def pubkeyhash_array(address): """Return PubKeyHashes from an address.""" signatures_required, pubs, signatures_possible = extract_array(address) if not all([is_pubkeyhash(pub) for pub in pubs]): raise MultiSigAddressError('Invalid PubKeyHashes. Multi signature address must use PubKeyHashes, not public keys.') pubkeyhashes = pubM return pubkeyhashes x = hashlib.sha256(x).digest() m = hashlib.new('ripemd160') return m.digest() def pubkey_to_pubkeyhash(pubkey): """Convert public key to PubKeyHash.""" pubkeyhash = hash160(pubkey) pubkey = base58_check_encode(binascii.hexlify(pubkeyhash).decode('utf-8'), config.ADDRESSVERSION) def pubkey_to_p2whash(pubkey): """Convert public key to PayToWitness.""" pubkeyhash = hash160(pubkey) pubkey = CBech32Data.froM m_bytes(0, pubkeyhash) return str(pubkey) def bech32_to_scripthash(address): bech32 = CBech32Data(address) return bytes(bech32) def get_asm(scriptpubkey): # TODO: When is an exception thrown here? Can this `try` block be tighter? Can it be replaced by a conditional? # TODO: This should be `for element in scriptpubkey`. for op in scriptpubkey: if type(op) == bitcoinlib.core.script.CScriptOp: # TODO: `op = element` asm.append(str(op)) # TODO: `data = element` (?) asm.append(op) except bitcoinlib.core.script.CScriptTruncatedPushDataError: raise exceptions.PushDataDecodeError('invalid pushdata due to truncation') raise exceptions.DecodeError('empty output') def get_checksig(asm): if len(asm) == 5 and asm[0] == 'OP_DUP' and asm[1] == 'OP_HASH160' and asm[3] == 'OP_EQUALVERIFY' and asm[4] == 'OP_CHECKSIG': if type(pubkeyhash) == bytes: return pubkeyhash raise exceptions.DecodeError('invalid OP_CHECKSIG') def get_checkmultisig(asm): if len(asm) == 5 and asm[3] == 2 and asm[4] == 'OP_CHECKMULTISIG': pubkeys, signatures_required = asm[1:3], asm[0] if all([type(pubkey) == bytes for pubkey in pubkeys]): return pubkeys, signatures_required if len(asm) == 6 and asm[4] == 3 and asm[5] == 'OP_CHECKMULTISIG': s, signatures_required = asm[1:4], asm[0] if all([type(pubkey) == bytes for pubkey in pubkeys]): return pubkeys, signatures_required raise exceptions.DecodeError('invalid OP_CHECKMULTISIG') def scriptpubkey_to_address(scriptpubkey): asm = get_asm(scriptpubkey) if asm[-1] == 'OP_CHECKSIG': checksig = get_checksig(asm) except exceptions.DecodeError: # coinbase return None return base58_check_encode(binascii.hexlify(checksig).decoM de('utf-8'), config.ADDRESSVERSION) elif asm[-1] == 'OP_CHECKMULTISIG': pubkeys, signatures_required = get_checkmultisig(asm) pubkeyhashes = [pubkey_to_pubkeyhash(pubkey) for pubkey in pubkeys] return construct_array(signatures_required, pubkeyhashes, len(pubkeyhashes)) elif len(asm) == 3 and asm[0] == 'OP_HASH160' and asm[2] == 'OP_EQUAL': return base58_check_encode(binascii.hexlify(asm[1]).decode('utf-8'), config.P2SH_ADDRESSVERSION) # TODO: Use `pythonM -bitcointools` instead. (Get rid of `pycoin` dependency.) from pycoin.encoding import wif_to_tuple_of_secret_exponent_compressed, public_pair_to_sec, EncodingError from pycoin.ecdsa import generator_secp256k1, public_pair_for_secret_exponent class AltcoinSupportError (Exception): pass def private_key_to_public_key(private_key_wif): """Convert private key to public key.""" if config.TESTNET: allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_TESTNET] elif config.REGTEST: allowable_wif_pM refixes = [config.PRIVATEKEY_VERSION_REGTEST] allowable_wif_prefixes = [config.PRIVATEKEY_VERSION_MAINNET] secret_exponent, compressed = wif_to_tuple_of_secret_exponent_compressed( private_key_wif, allowable_wif_prefixes=allowable_wif_prefixes) except EncodingError: raise AltcoinSupportError('pycoin: unsupported WIF prefix') public_pair = public_pair_for_secret_exponent(generator_secp256k1, secret_exponent) public_key = public_pair_to_sec(publicM _pair, compressed=compressed) public_key_hex = binascii.hexlify(public_key).decode('utf-8') return public_key_hex def is_pubkeyhash(monosig_address): """Check if PubKeyHash is valid P2PKH address. """ assert not is_multisig(monosig_address) base58_check_decode(monosig_address, config.ADDRESSVERSION) except (Base58Error, VersionByteError): return False def make_pubkeyhash(address): """Create a new PubKeyHash.""" if is_multisig(address): signatures_required, pubs, signatures_possible = extract_array(address) pubkeyhashes = [] for pub in pubs: if is_pubkeyhash(pub): pubkeyhash = pub pubkeyhash = pubkey_to_pubkeyhash(binascii.unhexlify(bytes(pub, 'utf-8'))) pubkeyhashes.append(pubkeyhash) pubkeyhash_address = construct_array(signatures_required, pubkeyhashes, signatures_possible) if util.enabled('segwit_support') and is_bech32(addresM pubkeyhash_address = address # Some bech32 addresses are valid base58 data elif is_pubkeyhash(address): pubkeyhash_address = address elif is_p2sh(address): pubkeyhash_address = address pubkeyhash_address = pubkey_to_pubkeyhash(binascii.unhexlify(bytes(address, 'utf-8'))) return pubkeyhash_address def extract_pubkeys(pub): """Assume pubkey if not pubkeyhash. (Check validity later.)""" if is_multisig(pub): _, pubs, _ = extract_array(pub) for pub in pubs: if not is_pubkeyhash(pub): pubkeys.append(pub) elif is_p2sh(pub): elif util.enabled('segwit_support') and is_bech32(pub): if not is_pubkeyhash(pub): pubkeys.append(pub) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 #### transaction.py Construct and serialize the Bitcoin transactions that are Counterparty transactions. This module contains no consensus logger = logging.getLogger(__name__) import bitcoin as bitcoinlib from bitcoin.core.script import CScript from bitcoin.core import x, CTransaction from bitcoin.core import b2lx from counterpartylib.lib import blocks from counterpartylib.lib import config from counterpartylib.lib import excM from counterpartylib.lib import util from counterpartylib.lib import script from counterpartylib.lib import backend from counterpartylib.lib import arc4 from counterpartylib.lib.transaction_helper import serializer, p2sh_encoding OP_PUSHDATA1 = b'\x4c' OP_HASH160 = b'\xa9' OP_EQUALVERIFY = b'\x88' OP_CHECKSIG = b'\xac' OP_CHECKMULTISIG = b'\xae' is None or DictCache per address # set higher than the max number of UTXOs we should expect to # manage in an aging cache for any one source address, at any one period UTXO_LOCKS_PER_ADDRESS_MAXSIZE = 5000 # UTXO_P2SH_ENCODING_LOCKS is TTLCache for UTXOs that are used for chaining p2sh encoding # instead of a simple (txid, vout) key we use [(vin.prevout.hash, vin.prevout.n) for vin tx1.vin] UTXO_P2SH_ENCODING_LOCKS = None # we cache the make_outkey_vin to avoid having to fetch raw txs too oftenM UTXO_P2SH_ENCODING_LOCKS_CACHE = None global UTXO_LOCKS, UTXO_P2SH_ENCODING_LOCKS, UTXO_P2SH_ENCODING_LOCKS_CACHE if config.UTXO_LOCKS_MAX_ADDRESSES > 0: # initialize if configured UTXO_LOCKS = util.DictCache(size=config.UTXO_LOCKS_MAX_ADDRESSES) UTXO_P2SH_ENCODING_LOCKS = cachetools.TTLCache(10000, 180) UTXO_P2SH_ENCODING_LOCKS_CACHE = cachetools.TTLCache(1000, 600) def print_coin(coin): return 'amount: {:.8f}; txid: {}; vout: {}; confirmations: {}'.format(coinM ['amount'], coin['txid'], coin['vout'], coin.get('confirmations', '?')) # simplify and make deterministic """ Yield successive n sized chunks from l. for i in range(0, len(l), n): yield l[i:i+n] def make_outkey(output): return '{}{}'.format(output['txid'], output['vout']) def make_outkey_vin_txid(txid, vout): global UTXO_P2SH_ENCODING_LOCKS_CACHE if (txid, vout) not in UTXO_P2SH_ENCODING_LOCKS_CACHE: txhex = backend.getrawtransaction(txid, verboM UTXO_P2SH_ENCODING_LOCKS_CACHE[(txid, vout)] = make_outkey_vin(txhex, vout) return UTXO_P2SH_ENCODING_LOCKS_CACHE[(txid, vout)] def make_outkey_vin(txhex, vout): txbin = binascii.unhexlify(txhex) if isinstance(txhex, str) else txhex assert isinstance(vout, int) tx = bitcoinlib.core.CTransaction.deserialize(txbin) outkey = [(vin.prevout.hash, vin.prevout.n) for vin in tx.vin] outkey = hashlib.sha256(("%s%s" % (outkey, vout)).encode('ascii')).digest() def get_dust_return_pubkey(source, provided_pubkeys, encoding): """Return the pubkey to which dust from data outputs will be sent. This pubkey is used in multi-sig data outputs (as the only real pubkey) to make those the outputs spendable. It is derived from the source address, so that the dust is spendable by the creator of the transaction. # Get hex dust return pubkey. if script.is_multisig(source): a, self_pubkeys, b = script.extract_array(backend.multisig_pubkeyhashes_tM o_pubkeys(source, provided_pubkeys)) dust_return_pubkey_hex = self_pubkeys[0] dust_return_pubkey_hex = backend.pubkeyhash_to_pubkey(source, provided_pubkeys) # Convert hex public key into the (binary) dust return pubkey. dust_return_pubkey = binascii.unhexlify(dust_return_pubkey_hex) except binascii.Error: raise script.InputError('Invalid private key.') return dust_return_pubkey def construct_coin_selection(encoding, data_array, source, allow_unconfM irmed_inputs, unspent_tx_hash, custom_inputs, fee_per_kb, estimate_fee_per_kb, estimate_fee_per_kb_nblocks, exact_fee, size_for_fee, fee_provided, destination_btc_out, data_btc_out, regular_dust_size, disable_utxo_locks): global UTXO_LOCKS, UTXO_P2SH_ENCODING_LOCKS # Array of UTXOs, as retrieved by listunspent function from bitcoind if custom_inputs: use_inputs = unspent = custom_inputs if unspent_tx_hash is not None: unspent = backend.get_unspent_txouts(source, unconfirmed=allow_unconfirmed_inputs, unspent_tx_hash=unspent_tx_hash) unspent = backend.get_unspent_txouts(source, unconfirmed=allow_unconfirmed_inputs) filter_unspents_utxo_locks = [] if UTXO_LOCKS is not None and source in UTXO_LOCKS: filter_unspents_utxo_locks = UTXO_LOCKS[source].keys() filter_unspents_p2sh_locks = UTXO_P2SH_ENCODING_LOCKS.keys() # filter out any locked UTXOs to prevM ent creating transactions that spend the same UTXO when they're created at the same time filtered_unspent = [] for output in unspent: if make_outkey(output) not in filter_unspents_utxo_locks and make_outkey_vin_txid(output['txid'], output['vout']) not in filter_unspents_p2sh_locks: filtered_unspent.append(output) unspent = filtered_unspent unspent = backend.sort_unspent_txouts(unspent) logger.debug('Sorted candidate UTXOs: {}'.format([print_coin(cM oin) for coin in unspent])) use_inputs = unspent # use backend estimated fee_per_kb if estimate_fee_per_kb: estimated_fee_per_kb = backend.fee_per_kb(estimate_fee_per_kb_nblocks, config.ESTIMATE_FEE_MODE) if estimated_fee_per_kb is not None: fee_per_kb = max(estimated_fee_per_kb, fee_per_kb) # never drop below the default fee_per_kb logger.debug('Fee/KB {:.8f}'.format(fee_per_kb / config.UNIT)) change_quantity = 0 final_fee = fee_per_kb desired_input_count = 1 if encoding == 'multisig' and data_array and util.enabled('bytespersigop'): desired_input_count = len(data_array) * 2 # pop inputs until we can pay for the fee for coin in use_inputs: logger.debug('New input: {}'.format(print_coin(coin))) inputs.append(coin) btc_in += round(coin['amount'] * config.UNIT) # If exact fee is specified, use that. Otherwise, calculate size of tx # and base M fee on that (plus provide a minimum fee for selling BTC). size = 181 * len(inputs) + size_for_fee + 10 if exact_fee: final_fee = exact_fee necessary_fee = int(size / 1000 * fee_per_kb) final_fee = max(fee_provided, necessary_fee) logger.getChild('p2shdebug').debug('final_fee inputs: %d size: %d final_fee %s' % (len(inputs), size, final_fee)) # Check if good. btc_out = destination_btc_out + data_btc_out change_quanM tity = btc_in - (btc_out + final_fee) logger.debug('Size: {} Fee: {:.8f} Change quantity: {:.8f} BTC'.format(size, final_fee / config.UNIT, change_quantity / config.UNIT)) # If change is necessary, must not be a dust output. if change_quantity == 0 or change_quantity >= regular_dust_size: sufficient_funds = True if len(inputs) >= desired_input_count: break if not sufficient_funds: # Approximate needed change, fee by with most recently calcM # quantities. btc_out = destination_btc_out + data_btc_out total_btc_out = btc_out + max(change_quantity, 0) + final_fee raise exceptions.BalanceError('Insufficient {} at address {}. (Need approximately {} {}.) To spend unconfirmed coins, use the flag `--unconfirmed`. (Unconfirmed coins cannot be spent from multi sig addresses.)'.format(config.BTC, source, total_btc_out / config.UNIT, config.BTC)) # Lock the source's inputs (UTXOs) chosen for this transaction O_LOCKS is not None and not disable_utxo_locks: if source not in UTXO_LOCKS: UTXO_LOCKS[source] = cachetools.TTLCache( UTXO_LOCKS_PER_ADDRESS_MAXSIZE, config.UTXO_LOCKS_MAX_AGE) for input in inputs: UTXO_LOCKS[source][make_outkey(input)] = input logger.debug("UTXO locks: Potentials ({}): {}, Used: {}, locked UTXOs: {}".format( len(unspent), [make_outkey(coin) for coin in unspent], [make_outkey(input) for input in inputs], listM (UTXO_LOCKS[source].keys()))) # ensure inputs have scriptPubKey # this is not provided by indexd inputs = backend.ensure_script_pub_key_for_inputs(inputs) return inputs, change_quantity, btc_in, final_fee def select_any_coin_from_source(source, allow_unconfirmed_inputs=True, disable_utxo_locks=False): ''' Get the first (biggest) input from the source address ''' global UTXO_LOCKS # Array of UTXOs, as retrieved by listunspent function from bitcoind unspent = backend.get_unspent_M txouts(source, unconfirmed=allow_unconfirmed_inputs) filter_unspents_utxo_locks = [] if UTXO_LOCKS is not None and source in UTXO_LOCKS: filter_unspents_utxo_locks = UTXO_LOCKS[source].keys() # filter out any locked UTXOs to prevent creating transactions that spend the same UTXO when they're created at the same time filtered_unspent = [] for output in unspent: if make_outkey(output) not in filter_unspents_utxo_locks: filtered_unspent.append(output) unspent = backend.sort_unspent_txouts(unspent) # use the first input input = unspent[0] if input is None: # Lock the source's inputs (UTXOs) chosen for this transaction if UTXO_LOCKS is not None and not disable_utxo_locks: if source not in UTXO_LOCKS: UTXO_LOCKS[source] = cachetools.TTLCache( UTXO_LOCKS_PER_ADDRESS_MAXSIZE, config.UTXO_LOCKS_MAX_AGE) UTXO_LOCKS[source][make_outkey(input)] = input def return_result(tx_hexes, old_style_api): tx_hexes = list(filter(None, tx_hexes)) # filter out None if old_style_api: if len(tx_hexes) != 1: raise Exception("Can't do 2 TXs with old_style_api") return tx_hexes[0] if len(tx_hexes) == 1: return tx_hexes[0] return tx_hexes def construct (db, tx_info, encoding='auto', fee_per_kb=config.DEFAULT_FEE_PER_M estimate_fee_per_kb=None, estimate_fee_per_kb_conf_target=config.ESTIMATE_FEE_CONF_TARGET, estimate_fee_per_kb_mode=config.ESTIMATE_FEE_MODE, estimate_fee_per_kb_nblocks=config.ESTIMATE_FEE_CONF_TARGET, regular_dust_size=config.DEFAULT_REGULAR_DUST_SIZE, multisig_dust_size=config.DEFAULT_MULTISIG_DUST_SIZE, op_return_value=config.DEFAULT_OP_RETURN_VALUE, exact_fee=None, fee_provided=0, provided_pubkeys=None, dust_return_puM allow_unconfirmed_inputs=False, unspent_tx_hash=None, custom_inputs=None, disable_utxo_locks=False, extended_tx_info=False, old_style_api=None, segwit=False, p2sh_source_multisig_pubkeys=None, p2sh_source_multisig_pubkeys_required=None, p2sh_pretx_txid=None,): if estimate_fee_per_kb is None: estimate_fee_per_kb = config.ESTIMATE_FEE_PER_KB global UTXO_LOCKS, UTXO_P2SH_ENCODING_LOCKS # lazy assign from config, because when set as default M it's evaluated before it's configured if old_style_api is None: old_style_api = config.OLD_STYLE_API (source, destination_outputs, data) = tx_info if dust_return_pubkey: dust_return_pubkey = binascii.unhexlify(dust_return_pubkey) if p2sh_source_multisig_pubkeys: p2sh_source_multisig_pubkeys = [binascii.unhexlify(p) for p in p2sh_source_multisig_pubkeys] # If public key is necessary for construction of (unsigned) # transaction, use the public M key provided, or find it from the # blockchain. script.validate(source) source_is_p2sh = script.is_p2sh(source) # Normalize source if script.is_multisig(source): source_address = backend.multisig_pubkeyhashes_to_pubkeys(source, provided_pubkeys) source_address = source # Sanity checks. if exact_fee and not isinstance(exact_fee, int): raise exceptions.TransactionError('Exact fees must be in satoshis.') if not isinstance(fee_pM raise exceptions.TransactionError('Fee provided must be in satoshis.') '''Determine encoding method''' desired_encoding = encoding # Data encoding methods (choose and validate). if desired_encoding == 'auto': if len(data) + len(config.PREFIX) <= config.OP_RETURN_MAX_SIZE: encoding = 'opreturn' encoding = 'p2sh' if not old_style_api and util.enabled('p2sh_encoding') else 'multisig' # p2sh M is not possible with old_style_api elif desired_encoding == 'p2sh' and not util.enabled('p2sh_encoding'): raise exceptions.TransactionError('P2SH encoding not enabled yet') elif encoding not in ('pubkeyhash', 'multisig', 'opreturn', 'p2sh'): raise exceptions.TransactionError('Unknown encoding encoding = None '''Destinations''' # Destination outputs. # Replace multi sig addresses with multi # destination output isn t a dust output. Set null values to dust size. destination_outputs_new = [] if encoding != 'p2sh': for (address, value) in destination_outputs: # Value. if script.is_multisig(address): dust_size = multisig_dust_size dust_size = regular_dust_size if value == None: value = dust_size elif value < dust_size: raise exceptions.TrM ansactionError('Destination output is dust.') # Address. script.validate(address) if script.is_multisig(address): destination_outputs_new.append((backend.multisig_pubkeyhashes_to_pubkeys(address, provided_pubkeys), value)) destination_outputs_new.append((address, value)) destination_outputs = destination_outputs_new destination_btc_out = sum([value for address, value in destination_outputs]) # @TODO: p2sh encoding require signable dust key if encoding == 'multisig': # dust_return_pubkey should be set or explicitly set to False to use the default configured for the node # the default for the node is optional so could fail if (source_is_p2sh and dust_return_pubkey is None) or (dust_return_pubkey is False and config.P2SH_DUST_RETURN_PUBKEY is None): raise exceptions.TransactionError("Can't use multisig encoding when source is P2SH and M no dust_return_pubkey is provided.") elif dust_return_pubkey is False: dust_return_pubkey = binascii.unhexlify(config.P2SH_DUST_RETURN_PUBKEY) if not dust_return_pubkey: if encoding == 'multisig' or encoding == 'p2sh' and not source_is_p2sh: dust_return_pubkey = get_dust_return_pubkey(source, provided_pubkeys, encoding) dust_return_pubkey = None # Divide data into chunks. if encoding == 'pubkeyhash': # Prefix is also a suffix here. chunk_size = 20 - 1 - 8 elif encoding == 'multisig': # Two pubkeys, minus length byte, minus prefix, minus two nonces, # minus two sign bytes. chunk_size = (33 * 2) - 1 - 8 - 2 - 2 elif encoding == 'p2sh': pubkeylength = -1 if dust_return_pubkey is not None: pubkeylength = len(dust_return_pubkey) chunk_size = p2sh_encoding.maximum_data_chunk_size(pubkeylengM elif encoding == 'opreturn': chunk_size = config.OP_RETURN_MAX_SIZE if len(data) + len(config.PREFIX) > chunk_size: raise exceptions.TransactionError('One `OP_RETURN` output per transaction.') data_array = list(chunks(data, chunk_size)) # Data outputs. if encoding == 'multisig': data_value = multisig_dust_size elif encoding == 'p2sh': data_value = 0 # this will be calculated later elif encoding == M data_value = op_return_value data_value = regular_dust_size data_output = (data_array, data_value) data_value = 0 data_array = [] data_output = None dust_return_pubkey = None data_btc_out = data_value * len(data_array) logger.getChild('p2shdebug').debug('data_btc_out=%s (data_value=%d len(data_array)=%d)' % (data_btc_out, data_value, len(data_array))) # Calculate collective size of outputs, for fee calculation. p2pkhsize = 25 + 9 if encoding == 'multisig': data_output_size = 81 # 71 for the data elif encoding == 'opreturn': # prefix + data + 10 bytes script overhead data_output_size = len(config.PREFIX) + 10 if data is not None: data_output_size = data_output_size + len(data) data_output_size = p2pkhsize # Pay PubKeyHash (25 for the dM outputs_size = (p2pkhsize * len(destination_outputs)) + (len(data_array) * data_output_size) if encoding == 'p2sh': # calculate all the p2sh outputs size_for_fee, datatx_necessary_fee, data_value, data_btc_out = p2sh_encoding.calculate_outputs(destination_outputs, data_array, fee_per_kb, exact_fee) # replace the data value data_output = (data_array, data_value) sum_data_output_size = len(data_array) * data_output_size size_for_fee = ((25 + 9) M * len(destination_outputs)) + sum_data_output_size if not (encoding == 'p2sh' and p2sh_pretx_txid): inputs, change_quantity, n_btc_in, n_final_fee = construct_coin_selection( encoding, data_array, source, allow_unconfirmed_inputs, unspent_tx_hash, custom_inputs, fee_per_kb, estimate_fee_per_kb, estimate_fee_per_kb_nblocks, exact_fee, size_for_fee, fee_provided, destination_btc_out, data_btc_out, regular_dust_size, disable_utxo_locks btc_in = n_btc_in final_fee = n_final_fee # when encoding is P2SH and the pretx txid is passed we can skip coinselection inputs, change_quantity = None, None if change_quantity: change_output = (source_address, change_quantity) change_output = None unsigned_pretx_hex = None unsigned_tx_hex = None pretx_txid = None if encoding == 'p2sh': assert not (segwit and p2sh_pretx_txid) # shouldn't do old stylM e with segwit enabled if p2sh_pretx_txid: pretx_txid = p2sh_pretx_txid if isinstance(p2sh_pretx_txid, bytes) else binascii.unhexlify(p2sh_pretx_txid) unsigned_pretx = None destination_value_sum = sum([value for (destination, value) in destination_outputs]) source_value = destination_value_sum if change_output: # add the difference between source and destination to the change change_value = change_outpM ut[1] + (destination_value_sum - source_value) change_output = (change_output[0], change_value) unsigned_pretx = serializer.serialise_p2sh_pretx(inputs, source=source_address, source_value=source_value, data_output=data_output, change_output=changM pubkey=dust_return_pubkey, multisig_pubkeys=p2sh_source_multisig_pubkeys, multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required) unsigned_pretx_hex = binascii.hexlify(unsigned_pretx).decode('utf-8') # with segwit we already know the txid and can return both #pretx_M txid = hashlib.sha256(unsigned_pretx).digest() # this should be segwit txid ptx = CTransaction.stream_deserialize(io.BytesIO(unsigned_pretx)) # could be a non-segwit tx anyways txid_ba = bytearray(ptx.GetTxid()) txid_ba.reverse() pretx_txid = bytes(txid_ba) # gonna leave the malleability problem to upstream logger.getChild('p2shdebug').debug('pretx_txid %s' % pretx_txid) print('pretx txid:', binascii.hexlify(pretx_txid)) if unsigned_pM # we set a long lock on this, don't want other TXs to spend from it UTXO_P2SH_ENCODING_LOCKS[make_outkey_vin(unsigned_pretx, 0)] = True # only generate the data TX if we have the pretx txId if pretx_txid: source_input = None if script.is_p2sh(source): source_input = select_any_coin_from_source(source) if not source_input: raise exceptions.TransactionError('Unable to select source input for p2shM unsigned_datatx = serializer.serialise_p2sh_datatx(pretx_txid, source=source_address, source_input=source_input, destination_outputs=destination_outputs, data_output=data_output, M pubkey=dust_return_pubkey, multisig_pubkeys=p2sh_source_multisig_pubkeys, multisig_pubkeys_required=p2sh_source_multisig_pubkeys_required) unsigned_datatx_hex = binascii.hexlify(unsigned_datatx).decode('utf-8') # let the rest of the code work it's magic on the data tx unsigned_tx_hex = unsigned_datatx_hex # we're just gonna M return the pretx, it doesn't require any of the further checks logger.warn('old_style_api = %s' % old_style_api) return return_result([unsigned_pretx_hex], old_style_api=old_style_api) # Serialise inputs and outputs. unsigned_tx = serializer.serialise(encoding, inputs, destination_outputs, data_output, change_output, dust_return_pubkey=dust_return_pubkey) unsigned_tx_hex = binascii.hexlify(unsiM gned_tx).decode('utf-8') '''Sanity Check''' # Desired transaction info. (desired_source, desired_destination_outputs, desired_data) = tx_info desired_source = script.make_canonical(desired_source) desired_destination = script.make_canonical(desired_destination_outputs[0][0]) if desired_destination_outputs else '' # NOTE: Include change in destinations for BTC transactions. # if change_output and not desired_data and desired_destination != config.UNSPENDABLE: # if desired_destinM # desired_destination = desired_source # desired_destination += '-{}'.format(desired_source) if desired_data == None: desired_data = b'' # Parsed transaction info. if pretx_txid and unsigned_pretx: backend.cache_pretx(pretx_txid, unsigned_pretx) parsed_source, parsed_destination, x, y, parsed_data, extra = blocks._get_tx_info(unsigned_tx_hex, p2sh_is_segwit=script.is_bech32(desired_source)) encoding == 'p2sh': # make_canonical can't determine the address, so we blindly change the desired to the parsed desired_source = parsed_source if pretx_txid and unsigned_pretx: backend.clear_pretx(pretx_txid) except exceptions.BTCOnlyError: if extended_tx_info: return { 'btc_in': btc_in, 'btc_out': destination_btc_out + data_btc_out, 'btc_change': change_quanM 'btc_fee': final_fee, 'tx_hex': unsigned_tx_hex, logger.getChild('p2shdebug').debug('BTC-ONLY') return return_result([unsigned_pretx_hex, unsigned_tx_hex], old_style_api=old_style_api) desired_source = script.make_canonical(desired_source) # Check desired info against parsed info. desired = (desired_source, desired_destination, desired_data) parsed = (parsed_source, parsed_destination, parsed_data) if desired != parsed: # Unlock (revert) UTXO locks if UTXO_LOCKS is not None and inputs: for input in inputs: UTXO_LOCKS[source].pop(make_outkey(input), None) raise exceptions.TransactionError('Constructed transaction does not parse correctly: {} {}'.format(desired, parsed)) if extended_tx_info: 'btc_in': btc_in, 'btc_out': destination_btc_out + data_btc_out, 'btc_change': change_quantity, 'btc_fee': final_fee, 'tx_hex': unsigned_tx_hex, return return_result([unsigned_pretx_hex, unsigned_tx_hex], old_style_api=old_style_api) def normalize_custom_inputs(raw_custom_inputs): custom_inputs = [] for custom_input in raw_custom_inputs: if 'value' not in custom_input: custom_input['value'] = int(custom_input['amount'] * config.UNIT) custom_inputs.append(custom_input) return custom_inputs # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 logger = logging.getLogger(__name__) from datetime import datetime from dateutil.tz import tzlocal from operator import itemgetter import bitcoin as bitcoinlib from counterpartylib.lib import exceptions from counterpartylib.lib.exceptions import DecodeEM from counterpartylib.lib import config B26_DIGITS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' # subasset contain only characters a-zA-Z0-9.-_@! SUBASSET_DIGITS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_@!' SUBASSET_REVERSE = {'a':1,'b':2,'c':3,'d':4,'e':5,'f':6,'g':7,'h':8,'i':9,'j':10,'k':11,'l':12,'m':13,'n':14, 'o':15,'p':16,'q':17,'r':18,'s':19,'t':20,'u':21,'v':22,'w':23,'x':24,'y':25,'z':26, 'A':27,'B':28,'C':29,'D':30,'E':31,'F':M 32,'G':33,'H':34,'I':35,'J':36,'K':37,'L':38,'M':39, 'N':40,'O':41,'P':42,'Q':43,'R':44,'S':45,'T':46,'U':47,'V':48,'W':49,'X':50,'Y':51,'Z':52, '0':53,'1':54,'2':55,'3':56,'4':57,'5':58,'6':59,'7':60,'8':61,'9':62,'.':63,'-':64,'_':65,'@':66,'!':67} # Obsolete in Python 3.4, with enum module. BET_TYPE_NAME = {0: 'BullCFD', 1: 'BearCFD', 2: 'Equal', 3: 'NotEqual'} BET_TYPE_ID = {'BullCFD': 0, 'BearCFD': 1, 'Equal': 2, 'NotEqual': 3} json_dump = lambda x: json.dumps(x, sortM _keys=True, indent=4) json_print = lambda x: print(json_dump(x)) CURRENT_BLOCK_INDEX = None CURR_DIR = os.path.dirname(os.path.realpath(__file__)) with open(CURR_DIR + '/../protocol_changes.json') as f: PROTOCOL_CHANGES = json.load(f) class RPCError (Exception): pass # TODO: Move to `util_test.py`. t timeout properly. (If server hangs, then unhangs, no result.) def api(method, params): """Poll API via JSON-RPC.""" headers = {'content-type': 'application/json'}M "method": method, "params": params, "jsonrpc": "2.0", response = requests.post(config.RPC, data=json.dumps(payload), headers=headers) if response == None: raise RPCError('Cannot communicate with {} server.'.format(config.XCP_NAME)) elif response.status_code != 200: if response.status_code == 500: raise RPCError('Malformed API call.') raise RPCError(str(response.status_code) + ' ' + respM response_json = response.json() if 'error' not in response_json.keys() or response_json['error'] == None: return response_json['result'] except KeyError: raise RPCError(response_json) raise RPCError('{} ({})'.format(response_json['error']['message'], response_json['error']['code'])) return [l[i:i + n] for i in range(0, len(l), n)] return [x for x in z] # Had to do it this way to support python 3.4, if we start # using the 3.5 runtime this can be replaced by: # (first_elem, *t) l.insert(0, first_elem) it = itertools.groupby(l, itemgetter(0)) for key, subiter in it: yield key, sum(item[1] for item in subiter) def date_passed(date): """Check if the date has already passed.""" return date <= int(time.time()) def price (numerator, denominator): eturn price as Fraction or Decimal.""" if CURRENT_BLOCK_INDEX >= 294500 or config.TESTNET or config.REGTEST: # Protocol change. return fractions.Fraction(numerator, denominator) numerator = D(numerator) denominator = D(denominator) return D(numerator / denominator) def last_message(db): """Return latest message from the db.""" cursor = db.cursor() messages = list(cursor.execute('''SELECT * FROM messages WHERE message_index = (SELECT MAX(message_index) from M assert len(messages) == 1 last_message = messages[0] raise exceptions.DatabaseError('No messages found.') return last_message def generate_asset_id(asset_name, block_index): """Create asset_id from asset_name.""" if asset_name == config.BTC: return 0 elif asset_name == config.XCP: return 1 if len(asset_name) < 4: raise exceptions.AssetNameError('too short') # Numeric asset names. umeric_asset_names'): # Protocol change. if asset_name[0] == 'A': # Must be numeric. asset_id = int(asset_name[1:]) except ValueError: raise exceptions.AssetNameError('non numeric asset name starts with # Number must be in range. if not (26**12 + 1 <= asset_id <= 2**64 - 1): raise exceptions.AssetNameError('numeric asset name not in range') return asset_id len(asset_name) >= 13: raise exceptions.AssetNameError('long asset names must be numeric') if asset_name[0] == 'A': raise exceptions.AssetNameError('non numeric asset name starts with # Convert the Base 26 string to an integer. for c in asset_name: if c not in B26_DIGITS: raise exceptions.AssetNameError('invalid character:', c) digit = B26_DIGITS.index(c) if asset_id < 26**3: raise exceptions.AssetNameError('too short') def generate_asset_name (asset_id, block_index): """Create asset_name from asset_id.""" if asset_id == 0: return config.BTC elif asset_id == 1: return config.XCP if asset_id < 26**3: raise exceptions.AssetIDError('too low') if enabled('numeric_asset_names'): # Protocol change. if asset_id <= 2**64 - 1: if 26**12 + 1 <= asset_id: asset_name = 'A' + str(asset_id) returnM raise exceptions.AssetIDError('too high') # Divide that integer into Base 26 string. n, r = divmod (n, 26) res.append(B26_DIGITS[r]) asset_name = ''.join(res[::-1]) return asset_name + checksum.compute(asset_name) return asset_name def get_asset_id (db, asset_name, block_index): """Return asset_id from asset_name.""" if not enabled('hotfix_numeric_assets'): generate_asset_id(asset_name, block_index) cursor = db.cursor() cursor.execute('''SELECT * FROM assets WHERE asset_name = ?''', (asset_name,)) assets = list(cursor) if len(assets) == 1: return int(assets[0]['asset_id']) raise exceptions.AssetError('No such asset: {}'.format(asset_name)) def get_asset_name (db, asset_id, block_index): """Return asset_name from asset_id.""" if not enabled('hotfix_numeric_assets'): return generate_asset_name(asset_id, block_inM cursor = db.cursor() cursor.execute('''SELECT * FROM assets WHERE asset_id = ?''', (str(asset_id),)) assets = list(cursor) if len(assets) == 1: return assets[0]['asset_name'] elif not assets: return 0 # Strange, I know # If asset_name is an existing subasset (PARENT.child) then return the corresponding numeric asset name (A12345) # If asset_name is not an existing subasset, then return the unmodified asset_name def resolve_subasset_longname(db, asset_name): nabled('subassets'): subasset_longname = None subasset_parent, subasset_longname = parse_subasset_from_asset_name(asset_name) except Exception as e: logger.warn("Invalid subasset {}".format(asset_name)) subasset_longname = None if subasset_longname is not None: cursor = db.cursor() cursor.execute('''SELECT asset_name FROM assets WHERE asset_longname = ?''', (subasset_longname,)) assets = list(cursor) cursor.close() if len(assets) == 1: return assets[0]['asset_name'] return asset_name # checks and validates subassets (PARENT.SUBASSET) # throws exceptions for assset or subasset names with invalid syntax # returns (None, None) if the asset is not a subasset name def parse_subasset_from_asset_name(asset): subasset_parent = None subasset_child = None subasset_longname = None chunks = asset.split('.', 1) if (len(chunks) == 2): subasset_parent = M subasset_child = chunks[1] subasset_longname = asset # validate parent asset validate_subasset_parent_name(subasset_parent) # validate child asset validate_subasset_longname(subasset_longname, subasset_child) return (subasset_parent, subasset_longname) # throws exceptions for invalid subasset names def validate_subasset_longname(subasset_longname, subasset_child=None): if subasset_child is None: chunks = subasset_longname.split('.', 1) if (len(chunks) == 2): subasset_child = chunks[1] subasset_child = '' if len(subasset_child) < 1: raise exceptions.AssetNameError('subasset name too short') if len(subasset_longname) > 250: raise exceptions.AssetNameError('subasset name too long') # can't start with period, can't have consecutive periods, can't contain anything not in SUBASSET_DIGITS previous_digit = '.' for c in subasset_child: if c not in SUBASSET_DIGITS: raise exceptions.AssetNameError('subasset name contains invalid character:', c) if c == '.' and previous_digit == '.': raise exceptions.AssetNameError('subasset name contains consecutive periods') previous_digit = c if previous_digit == '.': raise exceptions.AssetNameError('subasset name ends with a period') # throws exceptions for invalid subasset names def validate_subasset_parent_name(asset_name): if asset_name == config.BTC: xceptions.AssetNameError('parent asset cannot be {}'.format(config.BTC)) if asset_name == config.XCP: raise exceptions.AssetNameError('parent asset cannot be {}'.format(config.XCP)) if len(asset_name) < 4: raise exceptions.AssetNameError('parent asset name too short') if len(asset_name) >= 13: raise exceptions.AssetNameError('parent asset name too long') if asset_name[0] == 'A': raise exceptions.AssetNameError('parent asset name starts with if c not in B26_DIGITS: raise exceptions.AssetNameError('parent asset name contains invalid character:', c) def compact_subasset_longname(string): """Compacts a subasset name string into an array of bytes to save space using a base68 encoding scheme. Assumes all characters provided belong to SUBASSET_DIGITS. for i, c in enumerate(string[::-1]): name_int += (68 ** i) * SUBASSET_REVERSE[c] return name_int.to_bytes((name_int.M bit_length() + 7) // 8, byteorder='big') def expand_subasset_longname(raw_bytes): """Expands an array of bytes into a subasset name string.""" integer = int.from_bytes(raw_bytes, byteorder='big') if integer == 0: while integer != 0: ret = SUBASSET_DIGITS[integer % 68 - 1] + ret integer //= 68 def generate_random_asset (): return 'A' + str(random.randint(26**12 + 1, 2**64 - 1)) def parse_options_from_string(string): ions integer from string, if exists.""" string_list = string.split(" ") if len(string_list) == 2: options = int(string_list.pop()) raise exceptions.OptionsError('options not an integer') return options return False def validate_address_options(options): """Ensure the options are all valid and in range.""" if (options > config.MAX_INT) or (options < 0): raise exceptions.OptionsError('options integer overflow') elif options > config.ADDRESS_OPTION_MAX_VALUE: raise exceptions.OptionsError('options out of range') elif not active_options(config.ADDRESS_OPTION_MAX_VALUE, options): raise exceptions.OptionsError('options not possible') def active_options(config, options): """Checks if options active in some given config.""" return config & options == options class DebitError (Exception): pass def debit (db, address, asset, quantity, action=None, event=None): """Debit given address by quantity M block_index = CURRENT_BLOCK_INDEX if type(quantity) != int: raise DebitError('Quantity must be an integer.') if quantity < 0: raise DebitError('Negative quantity.') if quantity > config.MAX_INT: raise DebitError('Quantity can\'t be higher than MAX_INT.') if asset == config.BTC: raise DebitError('Cannot debit bitcoins.') debit_cursor = db.cursor() # Contracts can only hold XCP balances. if enabled('contracts_only_xcp_balances'): # ProtocM if len(address) == 40: assert asset == config.XCP if asset == config.BTC: raise exceptions.BalanceError('Cannot debit bitcoins from a {} address!'.format(config.XCP_NAME)) debit_cursor.execute('''SELECT * FROM balances \ WHERE (address = ? AND asset = ?)''', (address, asset)) balances = debit_cursor.fetchall() if not len(balances) == 1: old_balance = 0 old_balance = balances[0]['quantity'] raise DebitError('Insufficient funds.') balance = round(old_balance - quantity) balance = min(balance, config.MAX_INT) assert balance >= 0 'quantity': balance, 'address': address, 'asset': asset sql='update balances set quantity = :quantity where (address = :address and asset = :asset)' debit_cursor.execute(sql, bindings) 'block_index': block_index, 'address': address, 'asset': asset, 'quantity': quantity, 'action': action, 'event': event sql='insert into debits values(:block_index, :address, :asset, :quantity, :action, :event)' debit_cursor.execute(sql, bindings) debit_cursor.close() BLOCK_LEDGER.append('{}{}{}{}'.format(block_index, address, asset, quantity)) class CreditError (Exception): pass def credit (db, address, asset, quantity, action=None, event=None): """Credit given address by quantity of asset.""" index = CURRENT_BLOCK_INDEX if type(quantity) != int: raise CreditError('Quantity must be an integer.') if quantity < 0: raise CreditError('Negative quantity.') if quantity > config.MAX_INT: raise CreditError('Quantity can\'t be higher than MAX_INT.') if asset == config.BTC: raise CreditError('Cannot debit bitcoins.') credit_cursor = db.cursor() # Contracts can only hold XCP balances. if enabled('contracts_only_xcp_balances'): # Protocol change. if len(address) == 40: assert asset == config.XCP credit_cursor.execute('''SELECT * FROM balances \ WHERE (address = ? AND asset = ?)''', (address, asset)) balances = credit_cursor.fetchall() if len(balances) == 0: assert balances == [] #update balances table with new balance bindings = { 'address': address, 'asset': asset, 'quantity': quantity, sql='insert into balances values(:addrM ess, :asset, :quantity)' credit_cursor.execute(sql, bindings) elif len(balances) > 1: assert False old_balance = balances[0]['quantity'] assert type(old_balance) == int balance = round(old_balance + quantity) balance = min(balance, config.MAX_INT) bindings = { 'quantity': balance, 'address': address, 'asset': asset sql='update balances set quantity = :quantity where (address = :address and asM credit_cursor.execute(sql, bindings) # Record credit. 'block_index': block_index, 'address': address, 'asset': asset, 'quantity': quantity, 'action': action, 'event': event sql='insert into credits values(:block_index, :address, :asset, :quantity, :action, :event)' credit_cursor.execute(sql, bindings) credit_cursor.close() BLOCK_LEDGER.append('{}{}{}{}'.format(block_index, address, asset, quantity)) class QuantityError(Exception): pass def is_divisible(db, asset): """Check if the asset is divisible.""" if asset in (config.BTC, config.XCP): cursor = db.cursor() cursor.execute('''SELECT * FROM issuances \ WHERE (status = ? AND asset = ?) ORDER BY tx_index DESC''', ('valid', asset)) issuances = cursor.fetchall() if not issuances: raise exceptions.AssetError('No such asset: {}'.format(asset)) return issuances[M def value_input(quantity, asset, divisible): if asset == 'leverage': return round(quantity) if asset in ('value', 'fraction', 'price', 'odds'): return float(quantity) # TODO: Float?! quantity = D(quantity) * config.UNIT if quantity == quantity.to_integral(): return int(quantity) raise QuantityError('Divisible assets have only eight decimal places of precision.') quantity = D(quantityM if quantity != round(quantity): raise QuantityError('Fractional quantities of indivisible assets.') return round(quantity) def value_in(db, quantity, asset, divisible=None): if asset not in ['leverage', 'value', 'fraction', 'price', 'odds'] and divisible == None: divisible = is_divisible(db, asset) return value_input(quantity, asset, divisible) def value_output(quantity, asset, divisible): def norm(num, places): """Round only if necessary.""" = round(num, places) fmt = '{:.' + str(places) + 'f}' num = fmt.format(num) return num.rstrip('0')+'0' if num.rstrip('0')[-1] == '.' else num.rstrip('0') if asset == 'fraction': return str(norm(D(quantity) * D(100), 6)) + '%' if asset in ('leverage', 'value', 'price', 'odds'): return norm(quantity, 6) quantity = D(quantity) / D(config.UNIT) if quantity == quantity.to_integral(): return str(quantity) + '.0' # For divisiM ble assets, display the decimal point. return norm(quantity, 8) quantity = D(quantity) if quantity != round(quantity): raise QuantityError('Fractional quantities of indivisible assets.') return round(quantity) def value_out(db, quantity, asset, divisible=None): if asset not in ['leverage', 'value', 'fraction', 'price', 'odds'] and divisible == None: divisible = is_divisible(db, asset) return value_output(quantity, asset, divisiM def holders(db, asset, exclude_empty_holders=False): """Return holders of the asset.""" cursor = db.cursor() if exclude_empty_holders: cursor.execute('''SELECT * FROM balances \ WHERE asset = ? AND quantity > ?''', (asset, 0)) cursor.execute('''SELECT * FROM balances \ WHERE asset = ?''', (asset, )) for balance in list(cursor): holders.append({'address': balaM nce['address'], 'address_quantity': balance['quantity'], 'escrow': None}) # Funds escrowed in orders. (Protocol change.) cursor.execute('''SELECT * FROM orders \ WHERE give_asset = ? AND status = ?''', (asset, 'open')) for order in list(cursor): holders.append({'address': order['source'], 'address_quantity': order['give_remaining'], 'escrow': order['tx_hash']}) # Funds escrowed in pending order matches. (Protocol change.) cursor.execute('''SELECT * FROM order_matcheM WHERE (forward_asset = ? AND status = ?)''', (asset, 'pending')) for order_match in list(cursor): holders.append({'address': order_match['tx0_address'], 'address_quantity': order_match['forward_quantity'], 'escrow': order_match['id']}) cursor.execute('''SELECT * FROM order_matches \ WHERE (backward_asset = ? AND status = ?)''', (asset, 'pending')) for order_match in list(cursor): holders.append({'address': order_match['tx1_address'], 'addreM ss_quantity': order_match['backward_quantity'], 'escrow': order_match['id']}) # Bets and RPS (and bet/rps matches) only escrow XCP. if asset == config.XCP: cursor.execute('''SELECT * FROM bets \ WHERE status = ?''', ('open',)) for bet in list(cursor): holders.append({'address': bet['source'], 'address_quantity': bet['wager_remaining'], 'escrow': bet['tx_hash']}) cursor.execute('''SELECT * FROM bet_matches \ WHERE statusM = ?''', ('pending',)) for bet_match in list(cursor): holders.append({'address': bet_match['tx0_address'], 'address_quantity': bet_match['forward_quantity'], 'escrow': bet_match['id']}) holders.append({'address': bet_match['tx1_address'], 'address_quantity': bet_match['backward_quantity'], 'escrow': bet_match['id']}) cursor.execute('''SELECT * FROM rps \ WHERE status = ?''', ('open',)) for rps in list(cursor): holders.append({'adM dress': rps['source'], 'address_quantity': rps['wager'], 'escrow': rps['tx_hash']}) cursor.execute('''SELECT * FROM rps_matches \ WHERE status IN (?, ?, ?)''', ('pending', 'pending and resolved', 'resolved and pending')) for rps_match in list(cursor): holders.append({'address': rps_match['tx0_address'], 'address_quantity': rps_match['wager'], 'escrow': rps_match['id']}) holders.append({'address': rps_match['tx1_address'], 'address_quantity': rps_matcM h['wager'], 'escrow': rps_match['id']}) if enabled('dispensers_in_holders'): # Funds escrowed in dispensers. cursor.execute('''SELECT * FROM dispensers \ WHERE asset = ? AND status = ?''', (asset, 0)) for dispenser in list(cursor): holders.append({'address': dispenser['source'], 'address_quantity': dispenser['give_remaining'], 'escrow': None}) def xcp_created (db): """Return number of XCP created thus faM cursor = db.cursor() cursor.execute('''SELECT SUM(earned) AS total FROM burns \ WHERE (status = ?)''', ('valid',)) total = list(cursor)[0]['total'] or 0 def xcp_destroyed (db): """Return number of XCP destroyed thus far.""" cursor = db.cursor() cursor.execute('''SELECT SUM(quantity) AS total FROM destructions \ WHERE (status = ? AND asset = ?)''', ('valid', config.XCP)) al = list(cursor)[0]['total'] or 0 # Subtract issuance fees. cursor.execute('''SELECT SUM(fee_paid) AS total FROM issuances\ WHERE status = ?''', ('valid',)) issuance_fee_total = list(cursor)[0]['total'] or 0 # Subtract dividend fees. cursor.execute('''SELECT SUM(fee_paid) AS total FROM dividends\ WHERE status = ?''', ('valid',)) dividend_fee_total = list(cursor)[0]['total'] or 0 # Subtract sweep fees. cursor.execute('''SELECT SUM(fee_paid)M AS total FROM sweeps\ WHERE status = ?''', ('valid',)) sweeps_fee_total = list(cursor)[0]['total'] or 0 return destroyed_total + issuance_fee_total + dividend_fee_total + sweeps_fee_total def xcp_supply (db): """Return the XCP supply.""" return xcp_created(db) - xcp_destroyed(db) """Return creations.""" cursor = db.cursor() creations = {config.XCP: xcp_created(db)} cursor.execute('''SELECT asset, SUM(quantity) AS createdM WHERE status = ? GROUP BY asset''', ('valid',)) for issuance in cursor: asset = issuance['asset'] created = issuance['created'] creations[asset] = created return creations def destructions (db): """Return destructions.""" cursor = db.cursor() destructions = {config.XCP: xcp_destroyed(db)} cursor.execute('''SELECT asset, SUM(quantity) AS destroyed FROM destructions \ WHERE (status = ? AM ND asset != ?) GROUP BY asset''', ('valid', config.XCP)) for destruction in cursor: asset = destruction['asset'] destroyed = destruction['destroyed'] destructions[asset] = destroyed return destructions def asset_issued_total (db, asset): """Return asset total issued.""" cursor = db.cursor() cursor.execute('''SELECT SUM(quantity) AS total FROM issuances \ WHERE (status = ? AND asset = ?)''', ('valid', asset)) issued_total = lM ist(cursor)[0]['total'] or 0 return issued_total def asset_destroyed_total (db, asset): """Return asset total destroyed.""" cursor = db.cursor() cursor.execute('''SELECT SUM(quantity) AS total FROM destructions \ WHERE (status = ? AND asset = ?)''', ('valid', asset)) destroyed_total = list(cursor)[0]['total'] or 0 return destroyed_total def asset_supply (db, asset): """Return asset supply.""" return asset_issued_total(db, M asset) - asset_destroyed_total(db, asset) """Return supplies.""" d1 = creations(db) d2 = destructions(db) return {key: d1[key] - d2.get(key, 0) for key in d1.keys()} def held (db): #TODO: Rename ? "SELECT asset, SUM(quantity) AS total FROM balances GROUP BY asset", "SELECT give_asset AS asset, SUM(give_remaining) AS total FROM orders WHERE status = 'open' GROUP BY asset", "SELECT give_asset AS asset, SUM(give_remaining) AS total FROM orderM s WHERE status = 'filled' and give_asset = 'XCP' and get_asset = 'BTC' GROUP BY asset", "SELECT forward_asset AS asset, SUM(forward_quantity) AS total FROM order_matches WHERE status = 'pending' GROUP BY asset", "SELECT backward_asset AS asset, SUM(backward_quantity) AS total FROM order_matches WHERE status = 'pending' GROUP BY asset", "SELECT 'XCP' AS asset, SUM(wager_remaining) AS total FROM bets WHERE status = 'open'", "SELECT 'XCP' AS asset, SUM(forward_quantity) AS total FROM beM t_matches WHERE status = 'pending'", "SELECT 'XCP' AS asset, SUM(backward_quantity) AS total FROM bet_matches WHERE status = 'pending'", "SELECT 'XCP' AS asset, SUM(wager) AS total FROM rps WHERE status = 'open'", "SELECT 'XCP' AS asset, SUM(wager * 2) AS total FROM rps_matches WHERE status IN ('pending', 'pending and resolved', 'resolved and pending')", "SELECT asset, SUM(give_remaining) AS total FROM dispensers WHERE status=0 OR status=1 GROUP BY asset", sql = "SELECT asM set, SUM(total) AS total FROM (" + " UNION ALL ".join(queries) + ") GROUP BY asset;" cursor = db.cursor() cursor.execute(sql) for row in cursor: asset = row['asset'] total = row['total'] held[asset] = total class GetURLError (Exception): pass def get_url(url, abort_on_error=False, is_json=True, fetch_timeout=5): """Fetch URL using requests.get.""" r = requests.get(url, timeout=fetch_timeout) raise GetURLError("Got get_url request error: %s" % e) if r.status_code != 200 and abort_on_error: raise GetURLError("Bad status code returned: '%s'. result body: '%s'." % (r.status_code, r.text)) result = json.loads(r.text) if is_json else r.text if not isinstance(text, bytes): text = bytes(str(text), 'utf-8') return hashlib.sha256(hashlib.sha256(text).digest()).digest() def dhash_string(text): rn binascii.hexlify(dhash(text)).decode() def get_balance (db, address, asset): """Get balance of contract or address.""" cursor = db.cursor() balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (address, asset))) if not balances: return 0 else: return balances[0]['quantity'] # Why on Earth does `binascii.hexlify()` return bytes?! """Return the hexadecimal representation of the binary data. Decode from ASCII to M return binascii.hexlify(x).decode('ascii') def unhexlify(hex_string): return binascii.unhexlify(bytes(hex_string, 'utf-8')) ### Protocol Changes ### def enabled(change_name, block_index=None): """Return True if protocol change is enabled.""" if config.REGTEST: return True # All changes are always enabled on REGTEST if config.TESTNET: index_name = 'testnet_block_index' index_name = 'block_index' enable_block_index = PROTOCOL_CHANGES[change_name][M if not block_index: block_index = CURRENT_BLOCK_INDEX if block_index >= enable_block_index: return False def get_value_by_block_index(change_name, block_index=None): if not block_index: block_index = CURRENT_BLOCK_INDEX if config.REGTEST: max_block_index_testnet = -1 for key, value in PROTOCOL_CHANGES[change_name]["testnet"]: if int(key) > int(max_block_index): max_block_index = keyM return PROTOCOL_CHANGES[change_name]["testnet"][max_block_index]["value"] if config.TESTNET: index_name = 'testnet' index_name = 'mainnet' max_block_index = -1 for key in PROTOCOL_CHANGES[change_name][index_name]: if int(key) > int(max_block_index) and block_index >= int(key): max_block_index = key return PROTOCOL_CHANGES[change_name][index_name][max_block_index]["value"] def transfer(db, source, destinatM ion, asset, quantity, action, event): """Transfer quantity of asset from source to destination.""" debit(db, source, asset, quantity, action=action, event=event) credit(db, destination, asset, quantity, action=action, event=event) def make_id(hash_1, hash_2): return hash_1 + ID_SEPARATOR + hash_2 def parse_id(match_id): assert match_id[64] == ID_SEPARATOR return match_id[:64], match_id[65:] # UTF-8 encoding means that the indices are doubled. ance(v, dict) or isinstance(v, DictCache): for dk, dv in v.items(): s += sizeof(dk) s += sizeof(dv) return sys.getsizeof(v) """Threadsafe FIFO dict cache""" def __init__(self, size=100): if int(size) < 1 : raise AttributeError('size < 1 or not a number') self.size = size self.dict = collections.OrderedDict() self.lock = threading.Lock() def __getitem__(self,keyM with self.lock: return self.dict[key] def __setitem__(self,key,value): with self.lock: while len(self.dict) >= self.size: self.dict.popitem(last=False) self.dict[key] = value def __delitem__(self,key): with self.lock: del self.dict[key] def __len__(self): with self.lock: return len(self.dict) def __contains__(self, key): with self.lock: return key in self.dict ef refresh(self, key): with self.lock: self.dict.move_to_end(key, last=True) URL_USERNAMEPASS_REGEX = re.compile('.+://(.+)@') def clean_url_for_log(url): m = URL_USERNAMEPASS_REGEX.match(url) if m and m.group(1): url = url.replace(m.group(1), 'XXXXXXXX') # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 def satoshirate_to_fiat(satoshirate): return round(satoshirate/100.0,2) def get_oracle_last_price(db, oracle_address, block_index): cursor.execute('SELECT * FROM broadcasts WHERE source=:source AND status=:status AND block_index<:block_index ORDER by tx_index DESC LIMIT 1', { 'source': oracle_address, 'status': 'valid', 'block_index': block_index broadcasts = cursor.fetchall() if len(broadcasts) == 0: return None, None oracle_broadcast = broadcasts[0] oracle_label = oracle_broadcast["text"].split("-") if len(oracle_label) == 2: fiat_label = oracle_label[1] fiat_label = "" return oracle_broadcast['value'], oracle_broadcast['fee_fraction_int'], fiat_label, oracle_broadcast['block_index'] #### util_windows.py import logging.handlers logger = logging.getLogger(__name__) from ctypes import WINFUNCTYPE, windll, POINTER, byref, c_int from ctypes.wintypes import BOOL, HANDLE, DWORD, LPWSTR, LPCWSTR, LPVOID class SanitizedRotatingFileHanM dler(logging.handlers.RotatingFileHandler): def emit(self, record): # If the message doesn't need to be rendered we take a shortcut. if record.levelno < self.level: # Make sure the message is a string. message = record.msg #Sanitize and clean up the message message = unicodedata.normalize('NFKD', message).encode('ascii', 'ignore').decode() # Copy the original record so we don't break other handlers. record = copy.copy(record) record.msg = message # Use the built-in stream handler to handle output. logging.handlers.RotatingFileHandler.emit(self, record) def fix_win32_unicode(): """Thanks to http://stackoverflow.com/a/3259271 ! (converted to python3)""" if sys.platform != "win32": original_stderr = sys.stderr # If any exception occurs in this code, we'll probably try to print it on stderr, # which makes for frustrating debugging if stderr is directed to our wrapper. ranoid about catching errors and reporting them to original_stderr, # so that we can at least see them. def _complain(message): print(message if isinstance(message, str) else repr(message), file=original_stderr) # Work around <http://bugs.python.org/issue6058>. codecs.register(lambda name: codecs.lookup('utf-8') if name == 'cp65001' else None) # Make Unicode console output work independently of the current code page. # This also fixes <http://bugs.python.org/issue1602>. t to Michael Kaplan <http://blogs.msdn.com/b/michkap/archive/2010/04/07/9989346.aspx> # and TZOmegaTZIOY # <http://stackoverflow.com/questions/878972/windows-cmd-encoding-change-causes-python-crash/1432462#1432462>. # <http://msdn.microsoft.com/en-us/library/ms683231(VS.85).aspx> # HANDLE WINAPI GetStdHandle(DWORD nStdHandle); # returns INVALID_HANDLE_VALUE, NULL, or a valid handle # <http://msdn.microsoft.com/en-us/library/aa364960(VS.85).aspx> WORD WINAPI GetFileType(DWORD hFile); # <http://msdn.microsoft.com/en-us/library/ms683167(VS.85).aspx> # BOOL WINAPI GetConsoleMode(HANDLE hConsole, LPDWORD lpMode); GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(("GetStdHandle", windll.kernel32)) STD_OUTPUT_HANDLE = DWORD(-11) STD_ERROR_HANDLE = DWORD(-12) GetFileType = WINFUNCTYPE(DWORD, DWORD)(("GetFileType", windll.kernel32)) FILE_TYPE_CHAR = 0x0002 FILE_TYPE_REMOTE = 0x8000 eMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(("GetConsoleMode", windll.kernel32)) INVALID_HANDLE_VALUE = DWORD(-1).value def not_a_console(handle): if handle == INVALID_HANDLE_VALUE or handle is None: return True return ((GetFileType(handle) & ~FILE_TYPE_REMOTE) != FILE_TYPE_CHAR or GetConsoleMode(handle, byref(DWORD())) == 0) old_stdout_fileno = None old_stderr_fileno = None if hasattr(sys.stdout, 'fileno'): old_stdout_fileno = sys.stdout.fileno() if hasattr(sys.stderr, 'fileno'): old_stderr_fileno = sys.stderr.fileno() STDOUT_FILENO = 1 STDERR_FILENO = 2 real_stdout = (old_stdout_fileno == STDOUT_FILENO) real_stderr = (old_stderr_fileno == STDERR_FILENO) if real_stdout: hStdout = GetStdHandle(STD_OUTPUT_HANDLE) if not_a_console(hStdout): real_stdout = False if real_stderr: hStderr = GeM tStdHandle(STD_ERROR_HANDLE) if not_a_console(hStderr): real_stderr = False if real_stdout or real_stderr: # BOOL WINAPI WriteConsoleW(HANDLE hOutput, LPWSTR lpBuffer, DWORD nChars, # LPDWORD lpCharsWritten, LPVOID lpReserved); WriteConsoleW = WINFUNCTYPE(BOOL, HANDLE, LPWSTR, DWORD, POINTER(DWORD), LPVOID)(("WriteConsoleW", windll.kernel32)) class UnicodeOutput: def __init__(self, hConsoM le, stream, fileno, name): self._hConsole = hConsole self._stream = stream self._fileno = fileno self.closed = False self.softspace = False self.mode = 'w' self.encoding = 'utf-8' self.name = name self.errors = '' self.flush() def isatty(self): return False def closM # don't really close the handle, that would only cause problems self.closed = True def fileno(self): return self._fileno def flush(self): if self._hConsole is None: try: self._stream.flush() except Exception as e: _complain("%s.flush: %r from %r" % (self.name, e, self._stream)) def write(self, text): try: if self._hConsole is None: if isinstance(text, str): text = text.encode('utf-8') self._stream.write(text) else: if not isinstance(text, str): text = str(text).decode('utf-8') remaining = len(text) while remaining: n = DWORD(0) # There is a shorter-than-documented limitation on the # length of the string passed to WriteConsoleW (see # <http://tahoe-lafs.org/trac/tahoe-lafs/ticket/1232>. retval = WriteConsoleW(self._hConsole, text, min(remaining, 10000), byref(n), None) if retval == 0 or n.value == 0: raise IOError("WriteConsoleW returned %r, n.value = %r" % (retval, n.value)) remaining -= n.value if not remaining: break text = text[n.value:] except Exception as e: _complain("%s.write: %r" % (self.name, e)) raise def writelines(self, lines): try: for line in lines: self.write(line) except Exception as e: _complain("%s.writelines: %r" % (self.name, e)) raise if real_stdout: sys.stdout = UnicodeOutput(hStdout, None, STDOUT_FILENO, '<Unicode console stdout>') sys.stdout = UnicodeOutput(None, sys.stdout, old_stdout_fileno, '<Unicode redirected stdout>') if real_stderr: sys.stderr = UnicodeOutput(hStderr, None, STDERR_FILENO, '<Unicode console stderr>') sys.stderr = UnicodeOutput(None, sys.stderr, old_stderr_fileno, '<Unicode redirected stderr>') except Exception as e: _complain("exception %r while fixing up sys.stdout and sys.stderr" % (e,)) # While we're at it, let's unmangle the command-line arguments: # This works around <http://bugs.python.org/issue2128>. GetCommandLineW = WINFUNCTYPE(LPWSTR)(("GetCommM andLineW", windll.kernel32)) CommandLineToArgvW = WINFUNCTYPE(POINTER(LPWSTR), LPCWSTR, POINTER(c_int))(("CommandLineToArgvW", windll.shell32)) argv_unicode = CommandLineToArgvW(GetCommandLineW(), byref(argc)) argv = [argv_unicode[i].encode('utf-8').decode('utf-8') for i in range(0, argc.value)] if not hasattr(sys, 'frozen'): # If this is an executable produced by py2exe or bbfreeze, then it will # have been invoked directly. Otherwise, unicode_argv[0] is the PM # interpreter, so skip that. argv = argv[1:] # Also skip option arguments to the Python interpreter. while len(argv) > 0: arg = argv[0] if not arg.startswith("-") or arg == "-": break argv = argv[1:] if arg == '-m': # sys.argv[0] should really be the absolute path of the module source, # but never mind break if arg == '-c': argv[0] = '-c' break #### addrindexrs.py logger = logging.getLogger(__name__) from requests.exceptions import Timeout, ReadTimeout, ConnectionError import concurrent.futures import bitcoin.wallet from counterpartylib.lib import config, util, address SOCKET_TIMEOUT = 5.0 BACKEND_PING_TIME = 30.0 raw_transactions_cache = util.DictCache(size=config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE) # used in getrawtransaction_batch() class BackendRPCError(Exception): class AddrIndexRsRPCError(Exception): def rpc_call(payload): """Calls to bitcoin core and returns the response""" url = config.BACKEND_URL for i in range(TRIES): response = requests.post(url, data=json.dumps(payloaM d), headers={'content-type': 'application/json'}, verify=(not config.BACKEND_SSL_NO_VERIFY), timeout=config.REQUESTS_TIMEOUT) if i > 0: logger.debug('Successfully connected.') except (Timeout, ReadTimeout, ConnectionError): logger.debug('Could not connect to backend at `{}`. (Try {}/{})'.format(util.clean_url_for_log(url), i+1, TRIES)) time.sleep(5) if response == None: if config.TESTNET: network =M elif config.REGTEST: network = 'regtest' network = 'mainnet' raise BackendRPCError('Cannot communicate with backend at `{}`. (server is set to run on {}, is backend?)'.format(util.clean_url_for_log(url), network)) elif response.status_code in (401,): raise BackendRPCError('Authorization error connecting to {}: {} {}'.format(util.clean_url_for_log(url), response.status_code, response.reason)) elif response.status_code not in (200, 500):M raise BackendRPCError(str(response.status_code) + ' ' + response.reason) # Handle json decode errors response_json = response.json() except json.decoder.JSONDecodeError as e: raise BackendRPCError('Received invalid JSON from backend with a response of {}'.format(str(response.status_code) + ' ' + response.reason)) # Batch query returns a list if isinstance(response_json, list): return response_json if 'error' not in response_json.keys() or response_jsonM return response_json['result'] elif response_json['error']['code'] == -5: # RPC_INVALID_ADDRESS_OR_KEY raise BackendRPCError('{} Is `txindex` enabled in {} Core?'.format(response_json['error'], config.BTC_NAME)) elif response_json['error']['code'] in [-28, -8, -2]: Block height out of range The network does not appear to fully agree! logger.debug('Backend not ready. Sleeping for ten seconds.') coin Core takes more than `sys.getrecursionlimit() * 10 = 9970` # seconds to start, this ll hit the maximum recursion depth limit. time.sleep(10) return rpc_call(payload) raise BackendRPCError('Error connecting to {}: {}'.format(util.clean_url_for_log(url), response_json['error'])) def rpc(method, params): "method": method, "params": params, "jsonrpc": "2.0", return rpc_call(payload) def rpc_batch(requesM responses = collections.deque() def make_call(chunk): #send a list of requests to bitcoind to be executed #note that this is list executed serially, in the same thread in bitcoind #e.g. see: https://github.com/bitcoin/bitcoin/blob/master/src/rpcserver.cpp#L939 responses.extend(rpc_call(chunk)) chunks = util.chunkify(request_list, config.RPC_BATCH_SIZE) with concurrent.futures.ThreadPoolExecutor(max_workers=config.BACKEND_RPC_BATCH_NUM_WORKERS) as executor: for chunk in chunks: executor.submit(make_call, chunk) return list(responses) def extract_addresses(txhash_list): logger.debug('extract_addresses, txs: %d' % (len(txhash_list), )) tx_hashes_tx = getrawtransaction_batch(txhash_list, verbose=True) return extract_addresses_from_txlist(tx_hashes_tx, getrawtransaction_batch) def extract_addresses_from_txlist(tx_hashes_tx, _getrawtransaction_batch): helper for extract_addresses, seperated so we can pass in a mocked _getrM awtransaction_batch for test purposes logger.debug('extract_addresses_from_txlist, txs: %d' % (len(tx_hashes_tx.keys()), )) tx_hashes_addresses = {} tx_inputs_hashes = set() # use set to avoid duplicates for tx_hash, tx in tx_hashes_tx.items(): tx_hashes_addresses[tx_hash] = set() for vout in tx['vout']: if 'addresses' in vout['scriptPubKey']: tx_hashes_addresses[tx_hash].update(tuple(vout['scriptPubKey']['addresses'])) tx_inputs_hashesM .update([vin['txid'] for vin in tx['vin']]) logger.debug('extract_addresses, input TXs: %d' % (len(tx_inputs_hashes), )) # chunk txs to avoid huge memory spikes for tx_inputs_hashes_chunk in util.chunkify(list(tx_inputs_hashes), config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE): raw_transactions = _getrawtransaction_batch(tx_inputs_hashes_chunk, verbose=True) for tx_hash, tx in tx_hashes_tx.items(): for vin in tx['vin']: vin_tx = raw_transactions.get(vin['txid'], M if not vin_tx: continue vout = vin_tx['vout'][vin['vout']] if 'addresses' in vout['scriptPubKey']: tx_hashes_addresses[tx_hash].update(tuple(vout['scriptPubKey']['addresses'])) return tx_hashes_addresses, tx_hashes_tx def getblockcount(): return rpc('getblockcount', []) def getblockhash(blockcount): return rpc('getblockhash', [blockcount]) def getblock(block_hash): return rpc('getblock', [block_hash, M def getrawtransaction(tx_hash, verbose=False, skip_missing=False): return getrawtransaction_batch([tx_hash], verbose=verbose, skip_missing=skip_missing)[tx_hash] def getrawmempool(): return rpc('getrawmempool', []) def fee_per_kb(conf_target, mode, nblocks=None): :param conf_target: :return: fee_per_kb in satoshis, or None when unable to determine if nblocks is None and conf_target is None: conf_target = nblocks feeperkb = rpc('estimatesmaM rtfee', [conf_target, mode]) if 'errors' in feeperkb and feeperkb['errors'][0] == 'Insufficient data or no feerate found': return int(max(feeperkb['feerate'] * config.UNIT, config.DEFAULT_FEE_PER_KB_ESTIMATE_SMART)) def sendrawtransaction(tx_hex): return rpc('sendrawtransaction', [tx_hex]) GETRAWTRANSACTION_MAX_RETRIES=2 monotonic_call_id = 0 def getrawtransaction_batch(txhash_list, verbose=False, skip_missing=False, _retry=0): _logger = logger.getChild("getrawtransaction_batM if len(txhash_list) > config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE: #don't try to load in more than BACKEND_RAW_TRANSACTIONS_CACHE_SIZE entries in a single call txhash_list_chunks = util.chunkify(txhash_list, config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE) for txhash_list_chunk in txhash_list_chunks: txes.update(getrawtransaction_batch(txhash_list_chunk, verbose=verbose, skip_missing=skip_missing)) tx_hash_call_id = {} noncached_txhashes = set() txhash_list = set(txhash_list) # payload for transactions not in cache for tx_hash in txhash_list: if tx_hash not in raw_transactions_cache: #call_id = binascii.hexlify(os.urandom(5)).decode('utf8') # Don't drain urandom global monotonic_call_id monotonic_call_id = monotonic_call_id + 1 call_id = "{}".format(monotonic_call_id) payload.append({ "method": 'getrawtransaction', "params": [tx_hash, 1], "jsonrpc": "2.0", "id": call_id noncached_txhashes.add(tx_hash) tx_hash_call_id[call_id] = tx_hash #refresh any/all cache entries that already exist in the cache, # so they're not inadvertently removed by another thread before we can consult them #(this assumes that the size of the working set for any given workload doesn't exceed the max size of the cache) for tx_hash in txhash_list.difference(noncacM raw_transactions_cache.refresh(tx_hash) _logger.debug("getrawtransaction_batch: txhash_list size: {} / raw_transactions_cache size: {} / # getrawtransaction calls: {}".format( len(txhash_list), len(raw_transactions_cache), len(payload))) # populate cache if len(payload) > 0: batch_responses = rpc_batch(payload) for response in batch_responses: if 'error' not in response or response['error'] is None: tx_hex = response['result'] tx_hash = tx_hash_call_id[response['id']] raw_transactions_cache[tx_hash] = tx_hex elif skip_missing and 'error' in response and response['error']['code'] == -5: raw_transactions_cache[tx_hash] = None logging.debug('Missing TX with no raw info skipped (txhash: {}): {}'.format( tx_hash_call_id.get(response.get('id', '??'), '??'), response['error'])) #TODO: this seems to happen for bogus tM ransactions? Maybe handle it more gracefully than just erroring out? raise BackendRPCError('{} (txhash:: {})'.format(response['error'], tx_hash_call_id.get(response.get('id', '??'), '??'))) # get transactions from cache for tx_hash in txhash_list: if verbose: result[tx_hash] = raw_transactions_cache[tx_hash] result[tx_hash] = raw_transactions_cache[tx_hash]['hex'] if raw_transactions_cache[tx_hash] isM except KeyError as e: #shows up most likely due to finickyness with addrindex not always returning results that we need... print("Key error in addrindexrs still exists!!!!!") _logger.warning("tx missing in rawtx cache: {} -- txhash_list size: {}, hash: {} / raw_transactions_cache size: {} / # rpc_batch calls: {} / txhash in noncached_txhashes: {} / txhash in txhash_list: {} -- list {}".format( e, len(txhash_list), hashlib.md5(json.dumps(list(txhashM _list)).encode()).hexdigest(), len(raw_transactions_cache), len(payload), tx_hash in noncached_txhashes, tx_hash in txhash_list, list(txhash_list.difference(noncached_txhashes)) )) if _retry < GETRAWTRANSACTION_MAX_RETRIES: #try again time.sleep(0.05 * (_retry + 1)) # Wait a bit, hitting the index non-stop may cause it to just break down... TODO: Better handling r = getrawtransaction_batch([tx_hash], verbose=verbose, skip_missing=skip_missing, _retry=_retM result[tx_hash] = r[tx_hash] raise #already tried again, give up class AddrIndexRsThread (threading.Thread): def __init__(self, host, port): threading.Thread.__init__(self) self.host = host self.port = port self.sock = None self.lastId = 0 self.message_to_send = None self.message_result = None self.is_killed = False logging.debug('AddrIndexRs thM self.send({"kill": True}) def connect(self): self.lastId = 0 logging.info('AddrIndexRs connecting...') self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(SOCKET_TIMEOUT) self.sock.connect((self.host, self.port)) logging.info('Error connecting to AddrIndexRs! Retrying in a few seconds') time.sleep(5.0) logging.info('Connected to AddrIndexRs!') break self.locker = threading.Condition() self.locker.acquire() self.connect() while self.locker.wait(): if not(self.is_killed) and self.message_to_send != None: msg = self.message_to_send self.message_to_send = None retry_count = 15 while retry_count > 0: has_sent = False while not(has_sent) and msg: try: logging.debug('AddrIndexRs sending') self.sock.send(msg) has_sent = True except Exception as e: #try: logging.debug('AddrIndexRs error:' + e) self.connect() #except Exception as e2: #logging.debug('AddrIndexRM s fatal error:' + e2) self.message_to_send = None data = b"" parsed = False while not(parsed): try: data = data + self.sock.recv(READ_BUF_SIZE) self.message_result = json.loads(data.decode('utf-8')) retry_count = 0 parsed = True logging.debug('AddrIndexRs Recv complete!') except socket.timeout: logging.debug('AddrIndexRs Recv timeout error sending: '+str(msg)) if retry_count <= 0: self.connect() self.message_result = None retry_count -= -1 except socket.error as e: logging.debug('AddrIndexRs Recv error:' + str(e)+' with msg '+str(msg)) self.M except Exception as e: logging.debug('AddrIndexRs Recv error:' + str(e)+' with msg '+str(msg)) if retry_count <= 0: raise e self.message_result = None retry_count -= 1 finally: self.locker.notify() self.locker.notify() self.sock.close() ging.debug('AddrIndexRs socket closed normally') def send(self, msg): self.locker.acquire() if not("kill" in msg): msg["id"] = self.lastId self.lastId += 1 self.message_to_send = (json.dumps(msg) + "\n").encode('utf8') self.locker.notify() self.locker.wait() self.locker.release() return self.message_result def ensure_addrindexrs_connected(): max_backoff = 5000 le _backend == None: _backend = AddrIndexRsThread(config.INDEXD_CONNECT, config.INDEXD_PORT) _backend.daemon = True _backend.start() _backend.send({ "method": "server.version", "params": [] except Exception as e: logger.debug(e) time.sleep(backoff) backoff = min(backoff * 1.5, max_backoff) def _script_pubkey_to_hash(spk): return hashlib.sha256(spk).digest()[::M def _address_to_hash(addr): script_pubkey = bitcoin.wallet.CBitcoinAddress(addr).to_scriptPubKey() return _script_pubkey_to_hash(script_pubkey) # Returns an array of UTXOS from an address in the following format # "txId": utxo_txid_hex, # "confirmations": num # [{"txId":"a0d12eb3716e2e70fd00525486ace0da2947f82d818b7be0285f16ff672cf237","vout":5,"height":647484,"value":30455293,"confirmations":2}] def unpack_outpoint(outpoint): txid, vout = outpoint.split(':') return (txid, int(vout)) def unpack_vout(outpoint, tx, block_count): vout = tx["vout"][outpoint[1]] if "confirmations" in tx and tx["confirmations"] > 0: height = block_count - tx["confirmations"] + 1 tx["confirmations"] = 0 "txId": tx["txid"], "vout": outpoint[1], "height": height, "value": int(round(vout["value"] * config.UNIT)), "confirmations": tx["confirmations"] def get_unspent_txouts(source): ensure_addrindexrs_connected() block_count = getblockcount() result = _backend.send({ "method": "blockchain.scripthash.get_utxos", "params": [_address_to_hash(source)] if not(result is None) and "result" in result: result = result["result"] result = [unpack_outpoint(x) for x in result] # each item on the result array is like # {"tx_hash": hex_encoded_hash} h = getrawtransaction_batch([x[0] for x in result], verbose=True, skip_missing=True) batch = [unpack_vout(outpoint, batch[outpoint[0]], block_count) for outpoint in result if outpoint[0] in batch] batch = [x for x in batch if x is not None] return batch # Returns transactions in the following format # "blockhash": hexstring, # "blocktime": num, # "confirmations": num, # "hash": hexstring, # "hex": hexstring, # "txid": hexstring, # "txinwitness": array of hex_witness_program, // Only if it's a witness-containing tx # "txid": hexstring, # "sequence": num, # "coinbase": X, // contents not important, this is only present if the tx is a coinbase # "scriptSig": { # "asm": asm_decompiled_program, # "hex": hex_program # "value": decimal, # "scriptPubKey": { # "type": string, # "reqSigs": num, # "hex": hexstring, // the program in hex # "asm": string, // the decompiled program # "addresses": [ ...list of found addresses on the program ] def search_raw_transactions(address, unconfirmed=True): ensure_addrindexrs_connected() hsh = _address_to_hash(address) txs = _backend.send({ "method": "blockchain.scripthash.getM "params": [hsh] batch = getrawtransaction_batch([x["tx_hash"] for x in txs], verbose=True) if not(unconfirmed): batch = [x for x in batch if x.height >= 0] # Returns the number of blocks the backend is behind the node def getindexblocksbehind(): # Addrindexrs never "gets behind" ensure_addrindexrs_connected() if '_backend' in globals(): _backend.stop() logger = logging.getLogger(__name__) from requests.exceptions import Timeout, ReadTimeout, ConnectionError import concurrent.futures from counterpartylib.lib import config, util raw_transactions_cache = util.DictCache(size=config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE) # used in getrawtransaction_batch() class BackendRPCError(Exception): class IndexdRPCError(Exception):M def rpc_call(payload): """Calls to bitcoin core and returns the response""" url = config.BACKEND_URL for i in range(TRIES): response = requests.post(url, data=json.dumps(payload), headers={'content-type': 'application/json'}, verify=(not config.BACKEND_SSL_NO_VERIFY), timeout=config.REQUESTS_TIMEOUT) if i > 0: logger.debug('Successfully connected.') except (TimeoM ut, ReadTimeout, ConnectionError): logger.debug('Could not connect to backend at `{}`. (Try {}/{})'.format(util.clean_url_for_log(url), i+1, TRIES)) time.sleep(5) if response == None: if config.TESTNET: network = 'testnet' elif config.REGTEST: network = 'regtest' network = 'mainnet' raise BackendRPCError('Cannot communicate with backend at `{}`. (server is set to run on {}, is backend?)'.format(util.clean_url_for_M elif response.status_code in (401,): raise BackendRPCError('Authorization error connecting to {}: {} {}'.format(util.clean_url_for_log(url), response.status_code, response.reason)) elif response.status_code not in (200, 500): raise BackendRPCError(str(response.status_code) + ' ' + response.reason) # Handle json decode errors response_json = response.json() except json.decoder.JSONDecodeError as e: raise BackendRPCError('Received invalid JM SON from backend with a response of {}'.format(str(response.status_code) + ' ' + response.reason)) # Batch query returns a list if isinstance(response_json, list): return response_json if 'error' not in response_json.keys() or response_json['error'] == None: return response_json['result'] elif response_json['error']['code'] == -5: # RPC_INVALID_ADDRESS_OR_KEY raise BackendRPCError('{} Is `txindex` enabled in {} Core?'.format(response_json['error'], config.BTC_NAME)) if response_json['error']['code'] in [-28, -8, -2]: Block height out of range The network does not appear to fully agree! logger.debug('Backend not ready. Sleeping for ten seconds.') # If Bitcoin Core takes more than `sys.getrecursionlimit() * 10 = 9970` # seconds to start, this ll hit the maximum recursion depth limit. time.sleep(10) return rpc_call(payload) raise BackendRPCError('Error connecting to {M }: {}'.format(util.clean_url_for_log(url), response_json['error'])) def rpc(method, params): "method": method, "params": params, "jsonrpc": "2.0", return rpc_call(payload) def rpc_batch(request_list): responses = collections.deque() def make_call(chunk): #send a list of requests to bitcoind to be executed #note that this is list executed serially, in the same thread in bitcoind #e.g. see: https://github.com/bitcoinM /bitcoin/blob/master/src/rpcserver.cpp#L939 responses.extend(rpc_call(chunk)) chunks = util.chunkify(request_list, config.RPC_BATCH_SIZE) with concurrent.futures.ThreadPoolExecutor(max_workers=config.BACKEND_RPC_BATCH_NUM_WORKERS) as executor: for chunk in chunks: executor.submit(make_call, chunk) return list(responses) def extract_addresses(txhash_list): logger.debug('extract_addresses, txs: %d' % (len(txhash_list), )) tx_hashes_tx = getrawtransaction_batch(txhashM _list, verbose=True) return extract_addresses_from_txlist(tx_hashes_tx, getrawtransaction_batch) def extract_addresses_from_txlist(tx_hashes_tx, _getrawtransaction_batch): helper for extract_addresses, seperated so we can pass in a mocked _getrawtransaction_batch for test purposes logger.debug('extract_addresses_from_txlist, txs: %d' % (len(tx_hashes_tx.keys()), )) tx_hashes_addresses = {} tx_inputs_hashes = set() # use set to avoid duplicates for tx_hash, tx in tx_hashM tx_hashes_addresses[tx_hash] = set() for vout in tx['vout']: if 'addresses' in vout['scriptPubKey']: tx_hashes_addresses[tx_hash].update(tuple(vout['scriptPubKey']['addresses'])) tx_inputs_hashes.update([vin['txid'] for vin in tx['vin']]) logger.debug('extract_addresses, input TXs: %d' % (len(tx_inputs_hashes), )) # chunk txs to avoid huge memory spikes for tx_inputs_hashes_chunk in util.chunkify(list(tx_inputs_hashes), config.BACKENDM _RAW_TRANSACTIONS_CACHE_SIZE): raw_transactions = _getrawtransaction_batch(tx_inputs_hashes_chunk, verbose=True) for tx_hash, tx in tx_hashes_tx.items(): for vin in tx['vin']: vin_tx = raw_transactions.get(vin['txid'], None) if not vin_tx: continue vout = vin_tx['vout'][vin['vout']] if 'addresses' in vout['scriptPubKey']: tx_hashes_addresses[tx_hash].update(tuple(vout['scriptPubKey']M return tx_hashes_addresses, tx_hashes_tx def getblockcount(): return rpc('getblockcount', []) def getblockhash(blockcount): return rpc('getblockhash', [blockcount]) def getblock(block_hash): return rpc('getblock', [block_hash, False]) def getrawtransaction(tx_hash, verbose=False, skip_missing=False): return getrawtransaction_batch([tx_hash], verbose=verbose, skip_missing=skip_missing)[tx_hash] def getrawmempool(): return rpc('getrawmempool', []) def fee_per_kb(conf_tarM get, mode, nblocks=None): :param conf_target: :return: fee_per_kb in satoshis, or None when unable to determine if nblocks is None and conf_target is None: conf_target = nblocks feeperkb = rpc('estimatesmartfee', [conf_target, mode]) if 'errors' in feeperkb and feeperkb['errors'][0] == 'Insufficient data or no feerate found': return int(max(feeperkb['feerate'] * config.UNIT, config.DEFAULT_FEE_PER_KB_ESTIMATE_SMART)) wtransaction(tx_hex): return rpc('sendrawtransaction', [tx_hex]) GETRAWTRANSACTION_MAX_RETRIES=2 def getrawtransaction_batch(txhash_list, verbose=False, skip_missing=False, _retry=0): _logger = logger.getChild("getrawtransaction_batch") if len(txhash_list) > config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE: #don't try to load in more than BACKEND_RAW_TRANSACTIONS_CACHE_SIZE entries in a single call txhash_list_chunks = util.chunkify(txhash_list, config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE) for txhash_list_chunk in txhash_list_chunks: txes.update(getrawtransaction_batch(txhash_list_chunk, verbose=verbose, skip_missing=skip_missing)) tx_hash_call_id = {} noncached_txhashes = set() txhash_list = set(txhash_list) # payload for transactions not in cache for tx_hash in txhash_list: if tx_hash not in raw_transactions_cache: call_id = binascii.hexlify(os.urandom(5)).decode('utf8') "method": 'getrawtransaction', "params": [tx_hash, 1], "jsonrpc": "2.0", "id": call_id noncached_txhashes.add(tx_hash) tx_hash_call_id[call_id] = tx_hash #refresh any/all cache entries that already exist in the cache, # so they're not inadvertently removed by another thread before we can consult them #(this assumes that the size of the working set for any given workload doesn't exceed tM he max size of the cache) for tx_hash in txhash_list.difference(noncached_txhashes): raw_transactions_cache.refresh(tx_hash) _logger.debug("getrawtransaction_batch: txhash_list size: {} / raw_transactions_cache size: {} / # getrawtransaction calls: {}".format( len(txhash_list), len(raw_transactions_cache), len(payload))) # populate cache if len(payload) > 0: batch_responses = rpc_batch(payload) for response in batch_responses: if 'error' not in response M or response['error'] is None: tx_hex = response['result'] tx_hash = tx_hash_call_id[response['id']] raw_transactions_cache[tx_hash] = tx_hex elif skip_missing and 'error' in response and response['error']['code'] == -5: raw_transactions_cache[tx_hash] = None logging.debug('Missing TX with no raw info skipped (txhash: {}): {}'.format( tx_hash_call_id.get(response.get('id', '??'), '??'), response['error']))M #TODO: this seems to happen for bogus transactions? Maybe handle it more gracefully than just erroring out? raise BackendRPCError('{} (txhash:: {})'.format(response['error'], tx_hash_call_id.get(response.get('id', '??'), '??'))) # get transactions from cache for tx_hash in txhash_list: if verbose: result[tx_hash] = raw_transactions_cache[tx_hash] result[tx_hash] = raM w_transactions_cache[tx_hash]['hex'] if raw_transactions_cache[tx_hash] is not None else None except KeyError as e: #shows up most likely due to finickyness with addrindex not always returning results that we need... _logger.warning("tx missing in rawtx cache: {} -- txhash_list size: {}, hash: {} / raw_transactions_cache size: {} / # rpc_batch calls: {} / txhash in noncached_txhashes: {} / txhash in txhash_list: {} -- list {}".format( e, len(txhash_list), hashlib.md5(json.dumps(lM ist(txhash_list)).encode()).hexdigest(), len(raw_transactions_cache), len(payload), tx_hash in noncached_txhashes, tx_hash in txhash_list, list(txhash_list.difference(noncached_txhashes)) )) if _retry < GETRAWTRANSACTION_MAX_RETRIES: #try again time.sleep(0.05 * (_retry + 1)) # Wait a bit, hitting the index non-stop may cause it to just break down... TODO: Better handling r = getrawtransaction_batch([tx_hash], verbose=verbose, skip_missing=skip_missing, _M result[tx_hash] = r[tx_hash] raise #already tried again, give up def get_unspent_txouts(source): return indexd_rpc_call('/a/'+source+'/utxos') def search_raw_transactions(address, unconfirmed=True): all_transactions = indexd_rpc_call('/a/'+address+'/txs?verbose=1') return all_transactions # filter for confirmed transactions only confirmed_transactions = list(filter(lambda t: 'confirmaM tions' in t and t['confirmations'] > 0, all_transactions)) return confirmed_transactions def getindexblocksbehind(): status = indexd_rpc_call('/status') if status['ready']: if status['blocksBehind']: return status['blocksBehind'] raise IndexdRPCError('Unknown status for indexd') def indexd_rpc_call(path): url = config.INDEXD_URL+path response = requests.get(url, headers={'content-type': 'application/json'}, t=config.REQUESTS_TIMEOUT) except (Timeout, ReadTimeout, ConnectionError): logger.debug('Could not connect to backend at `{}`.'.format(util.clean_url_for_log(url),)) if response == None: if config.TESTNET: network = 'testnet' elif config.REGTEST: network = 'regtest' network = 'mainnet' raise IndexdRPCError('Cannot communicate with {} indexd server at `{}`.'.format(network, util.clean_url_for_log(url))) elif response.statuM raise IndexdRPCError('Indexd returned error: {} {} {}'.format(response.status_code, response.reason, response.text)) elif response.status_code not in (200, 500): raise IndexdRPCError("Bad response from {}: {} {}".format(util.clean_url_for_log(url), response.status_code, response.reason)) # Return result, with error handling. response_json = response.json() if isinstance(response_json, (list, tuple)) or 'error' not in response_json.keys() or response_json['error'] == NM return response_json raise IndexdRPCError('{}'.format(response_json['error'])) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 logger = logging.getLogger(__name__) from decimal import Decimal as D import bitcoin as bitcoinlib import bitcoin.rpc as bitcoinlib_rpc from bitcoin.core import CBlock from counterpartylib.lib import util from counterpartylib.lib import script from counterpartylib.lib import config from counterpartylib.lib import exceptions from counterpartylib.lib.backend import addrindexrs MEMPOOL_CACHE_INITIALIZED = False def sortkeypicker(keynames): """http://stackoverflow.com/a/1143719""" for i, k in enumerate(keynames): if k[:1] == '-': keynames[i] = k[1:] negate.add(k[1:]) def getit(adict): composite = [adict[k] for k in keynames] for i, (k, v) in enumerate(zip(keynamM if k in negate: composite[i] = -v return composite mdl = sys.modules['counterpartylib.lib.backend.{}'.format(config.BACKEND_NAME)] BACKEND().stop() def getblockcount(): return BACKEND().getblockcount() def getblockhash(blockcount): return BACKEND().getblockhash(blockcount) def getblock(block_hash): block_hex = BACKEND().getblock(block_hash) return CBlock.deserializM e(util.unhexlify(block_hex)) def cache_pretx(txid, rawtx): PRETX_CACHE[binascii.hexlify(txid).decode('utf8')] = binascii.hexlify(rawtx).decode('utf8') def clear_pretx(txid): del PRETX_CACHE[binascii.hexlify(txid).decode('utf8')] def getrawtransaction(tx_hash, verbose=False, skip_missing=False): if tx_hash in PRETX_CACHE: return PRETX_CACHE[tx_hash] return BACKEND().getrawtransaction(tx_hash, verbose=verbose, skip_missing=skip_missing) def getrawtransaction_batch(txhash_listM , verbose=False, skip_missing=False): return BACKEND().getrawtransaction_batch(txhash_list, verbose=verbose, skip_missing=skip_missing) def sendrawtransaction(tx_hex): return BACKEND().sendrawtransaction(tx_hex) def getrawmempool(): return BACKEND().getrawmempool() def getindexblocksbehind(): return BACKEND().getindexblocksbehind() def extract_addresses(txhash_list): return BACKEND().extract_addresses(txhash_list) def ensure_script_pub_key_for_inputs(coins): txhash_set = set() if 'scriptPubKey' not in coin: txhash_set.add(coin['txid']) if len(txhash_set) > 0: txs = BACKEND().getrawtransaction_batch(list(txhash_set), verbose=True, skip_missing=False) for coin in coins: if 'scriptPubKey' not in coin: # get the scriptPubKey txid = coin['txid'] for vout in txs[txid]['vout']: if vout['n'] == coin['vout']: coin['scriptPubKey'] = vout['scM def fee_per_kb(conf_target, mode, nblocks=None): :param conf_target: :return: fee_per_kb in satoshis, or None when unable to determine return BACKEND().fee_per_kb(conf_target, mode, nblocks=nblocks) def deserialize(tx_hex): return bitcoinlib.core.CTransaction.deserialize(binascii.unhexlify(tx_hex)) return bitcoinlib.core.CTransaction.serialize(ctx) def is_valid(address): script.valiM except script.AddressError: return False def get_txhash_list(block): return [bitcoinlib.core.b2lx(ctx.GetHash()) for ctx in block.vtx] def get_tx_list(block): raw_transactions = {} tx_hash_list = [] for ctx in block.vtx: if util.enabled('correct_segwit_txids'): hsh = ctx.GetTxid() hsh = ctx.GetHash() tx_hash = bitcoinlib.core.b2lx(hsh) raw = ctx.serialize() tx_hash_list.append(tx_hM raw_transactions[tx_hash] = bitcoinlib.core.b2x(raw) return (tx_hash_list, raw_transactions) def sort_unspent_txouts(unspent, unconfirmed=False): # Filter out all dust amounts to avoid bloating the resultant transaction unspent = list(filter(lambda x: x['value'] > config.DEFAULT_MULTISIG_DUST_SIZE, unspent)) # Sort by amount, using the largest UTXOs available if config.REGTEST: # REGTEST has a lot of coinbase inputs that can't be spent due to maturity # this doesn'M t usually happens on mainnet or testnet because most fednodes aren't mining unspent = sorted(unspent, key=lambda x: (x['confirmations'], x['value']), reverse=True) unspent = sorted(unspent, key=lambda x: x['value'], reverse=True) def get_btc_supply(normalize=False): """returns the total supply of {} (based on what Bitcoin Core says the current block height is)""".format(config.BTC) block_count = getblockcount() blocks_remaining = block_count while blocks_remaining > 0: if blocks_remaining >= 210000: blocks_remaining -= 210000 total_supply += 210000 * reward reward /= 2 total_supply += (blocks_remaining * reward) blocks_remaining = 0 return total_supply if normalize else int(total_supply * config.UNIT) class MempoolError(Exception): def get_unspent_txouts(source, unconfirmed=False, unspent_tx_hash=None): """returns a list of uM nspent outputs for a specific address @return: A list of dicts, with each entry in the dict having the following keys: unspent = BACKEND().get_unspent_txouts(source) # filter by unspent_tx_hash if unspent_tx_hash is not None: unspent = list(filter(lambda x: x['txId'] == unspent_tx_hash, unspent)) # filter unconfirmed if not unconfirmed: unspent = [utxo for utxo in unspent if utxo['confirmations'] > 0] for utxo in unspent: utxo['amount'] = M float(utxo['value'] / config.UNIT) utxo['txid'] = utxo['txId'] del utxo['txId'] # do not add scriptPubKey def search_raw_transactions(address, unconfirmed=True): return BACKEND().search_raw_transactions(address, unconfirmed) class UnknownPubKeyError(Exception): def pubkeyhash_to_pubkey(pubkeyhash, provided_pubkeys=None): # Search provided pubkeys. if provided_pubkeys: if type(provided_pubkeys) != list: provided_pubkeys = [providM for pubkey in provided_pubkeys: if pubkeyhash == script.pubkey_to_pubkeyhash(util.unhexlify(pubkey)): return pubkey elif pubkeyhash == script.pubkey_to_p2whash(util.unhexlify(pubkey)): return pubkey # Search blockchain. raw_transactions = search_raw_transactions(pubkeyhash, unconfirmed=True) for tx_id in raw_transactions: tx = raw_transactions[tx_id] for vin in tx['vin']: if 'txinwitness' in vin: if len(vin['txinwitness']) >= 2: # catch unhexlify errs for when txinwitness[1] isn't a witness program (eg; for P2W) try: pubkey = vin['txinwitness'][1] if pubkeyhash == script.pubkey_to_p2whash(util.unhexlify(pubkey)): return pubkey except binascii.Error: pass elif 'coinbase' not in vin: scriptsig = vin['scriptSig']M asm = scriptsig['asm'].split(' ') if len(asm) >= 2: # catch unhexlify errs for when asm[1] isn't a pubkey (eg; for P2SH) try: pubkey = asm[1] if pubkeyhash == script.pubkey_to_pubkeyhash(util.unhexlify(pubkey)): return pubkey except binascii.Error: pass raise UnknownPubKeyError('Public key was neither provided nor publisM hed in blockchain.') def multisig_pubkeyhashes_to_pubkeys(address, provided_pubkeys=None): signatures_required, pubkeyhashes, signatures_possible = script.extract_array(address) pubkeys = [pubkeyhash_to_pubkey(pubkeyhash, provided_pubkeys) for pubkeyhash in pubkeyhashes] return script.construct_array(signatures_required, pubkeys, signatures_possible) def init_mempool_cache(): """prime the mempool cache, so that functioning is faster... global MEMPOOL_CACHE_INITIALIZED g('Initializing mempool cache...') start = time.time() mempool_txhash_list = getrawmempool() #with this function, don't try to load in more than BACKEND_RAW_TRANSACTIONS_CACHE_SIZE entries num_tx = min(len(mempool_txhash_list), config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE) mempool_tx = BACKEND().getrawtransaction_batch(mempool_txhash_list[:num_tx], skip_missing=True, verbose=True) vin_txhash_list = [] max_remaining_num_tx = config.BACKEND_RAW_TRANSACTIONS_CACHE_SIZE - num_tx for txid in mempool_tx: tx = mempool_tx[txid] if not(tx is None): vin_txhash_list += [vin['txid'] for vin in tx['vin']] BACKEND().getrawtransaction_batch(vin_txhash_list[:max_remaining_num_tx], skip_missing=True, verbose=True) MEMPOOL_CACHE_INITIALIZED = True logger.info('Mempool cache initialized: {:.2f}s for {:,} transactions'.format(time.time() - start, num_tx + min(max_remaining_num_tx, len(vin_txhash_list)))) expandtab shiftwidth=4 softtabstop=4 #### bc_data_stream.py # Workalike python implementation of Bitcoin's CDataStream class. from .exceptions import SerializationError class BCDataStream(object): def __init__(self): self.input = None self.read_cursor = 0 self.input = None self.read_cursor = 0 def write(self, bytes): # Initialize with string of bytes if self.input is None: self.input = bytes self.input += bM def map_file(self, file, start): # Initialize with bytes from file self.input = mmap.mmap(file.fileno(), 0, access=mmap.ACCESS_READ) self.read_cursor = start def seek_file(self, position): self.read_cursor = position def close_file(self): self.input.close() def read_string(self): # Strings are encoded depending on length: # 0 to 252 : 1-byte-length followed by bytes (if any) # 253 to 65,535 : byte'253' 2-byte-length followed by bytes # 65,536 to 4,294,967,295 : bytM e '254' 4-byte-length followed by bytes # ... and the Bitcoin client is coded to understand: # greater than 4,294,967,295 : byte '255' 8-byte-length followed by bytes of string # ... but I don't think it actually handles any strings that big. if self.input is None: raise SerializationError("call write(bytes) before trying to deserialize") length = self.read_compact_size() except IndexError: raise SerializationError("attempt to read past end of buffer") f.read_bytes(length) def write_string(self, string): # Length-encoded as with read-string self.write_compact_size(len(string)) self.write(string) def read_bytes(self, length): result = self.input[self.read_cursor:self.read_cursor+length] self.read_cursor += length except IndexError: raise SerializationError("attempt to read past end of buffer") def read_boolean(self): return self.read_bytes(1)[0] != chr(0) def read_int16(self):M return self._read_num('<h') def read_uint16(self): return self._read_num('<H') def read_int32(self): return self._read_num('<i') def read_uint32(self): return self._read_num('<I') def read_int64(self): return self._read_num('<q') def read_uint64(self): return self._read_num('<Q') def write_boolean(self, val): return self.write(chr(1) if val else chr(0)) def write_int16(self, val): return self._write_num('<h', val) def write_uint16(self, val): return self._write_num('<H', val) def write_int32(selM f, val): return self._write_num('<i', val) def write_uint32(self, val): return self._write_num('<I', val) def write_int64(self, val): return self._write_num('<q', val) def write_uint64(self, val): return self._write_num('<Q', val) def read_compact_size(self): size = self.input[self.read_cursor] self.read_cursor += 1 size = self._read_num('<H') elif size == 254: size = self._read_num('<I') elif size == 255: size = self._read_num('<Q') def write_compact_size(self, size): raise SerializationError("attempt to write size < 0") elif size < 253: self.write(chr(size)) elif size < 2**16: self.write('\xfd') self._write_num('<H', size) elif size < 2**32: self.write('\xfe') self._write_num('<I', size) elif size < 2**64: self.write('\xff') self._write_num('<Q', size) def _read_num(self, format): (i,) = struct.unpack_from(format, self.input, self.read_cursor) ad_cursor += struct.calcsize(format) def _write_num(self, format, num): s = struct.pack(format, num) def read_var_int(self): cur_byte = self.read_bytes(1)[0] n = (n << 7) | (cur_byte & 0x7F) if cur_byte & 0x80: return n #### blocks_parser.py import os, json, time, logging, binascii logger = logging.getLogger(__name__) from .bc_data_stream import BCDataStrM from .utils import b2h, double_hash, ib2h, inverse_hash def open_leveldb(db_dir): import plyvel raise Exception("Please install the plyvel package via pip3.") return plyvel.DB(db_dir, create_if_missing=False) except plyvel._plyvel.IOError as e: logger.info(str(e)) raise Exception("Ensure that bitcoind is stopped.") class BlockchainParser(): def __init__(self, blocks_dir, leveldb_dir): self.blocks_dir = blocks_dir self.leveldb_dir = leveldb_dir self.file_num = -1 self.current_file_size = 0 self.current_block_file = None self.data_stream = None self.ldb = open_leveldb(self.leveldb_dir) def read_tx_in(self, vds): tx_in['txid'] = ib2h(vds.read_bytes(32)) tx_in['vout'] = vds.read_uint32() script_sig_size = vds.read_compact_size() tx_in['scriptSig'] = b2h(vds.read_bytes(script_sig_size)) tx_in['sequence'] = vds.read_uint32() if tx_in['txid'] == '0000000000000000000000000000000000000000000000000000000000000000': tx_in = { 'coinbase': tx_in['scriptSig'], 'sequence': tx_in['sequence'] return tx_in def read_tx_out(self, vds): tx_out['value'] = vds.read_int64() / 100000000 script = vds.read_bytes(vds.read_compact_size()) tx_out['scriptPubKey'] = { 'hex': b2h(script) return tx_out d_transaction(self, vds): transaction = {} start_pos = vds.read_cursor transaction['version'] = vds.read_int32() transaction['vin'] = [] for i in range(vds.read_compact_size()): transaction['vin'].append(self.read_tx_in(vds)) transaction['vout'] = [] for i in range(vds.read_compact_size()): transaction['vout'].append(self.read_tx_out(vds)) transaction['lock_time'] = vds.read_uint32() data = vds.input[start_pos:vds.reaM transaction['tx_hash'] = ib2h(double_hash(data)) transaction['__data__'] = b2h(data) return transaction def read_block_header(self, vds): block_header = {} block_header['magic_bytes'] = vds.read_int32() #if block_header['magic_bytes'] != 118034699: # raise Exception('Not a block') block_header['block_size'] = vds.read_int32() header_start = vds.read_cursor block_header['version'] = vds.read_int32() block_headerM ['hash_prev'] = ib2h(vds.read_bytes(32)) block_header['hash_merkle_root'] = ib2h(vds.read_bytes(32)) block_header['block_time'] = vds.read_uint32() block_header['bits'] = vds.read_uint32() block_header['nonce'] = vds.read_uint32() header_end = vds.read_cursor header = vds.input[header_start:header_end] block_header['block_hash'] = ib2h(double_hash(header)) block_header['__header__'] = b2h(header) return block_header def read_block(self, vdM block = self.read_block_header(vds) block['transaction_count'] = vds.read_compact_size() block['transactions'] = [] for i in range(block['transaction_count']): block['transactions'].append(self.read_transaction(vds)) return block def prepare_data_stream(self, file_num, pos_in_file): if self.data_stream is None or file_num != self.file_num: self.file_num = file_num if self.current_block_file: self.current_bloM data_file_path = os.path.join(self.blocks_dir, 'blk%05d.dat' % (self.file_num,)) self.current_block_file = open(data_file_path, "rb") self.data_stream = BCDataStream() self.data_stream.map_file(self.current_block_file, pos_in_file) self.data_stream.seek_file(pos_in_file) def read_raw_block(self, block_hash): block_hash = binascii.unhexlify(inverse_hash(block_hash)) block_data = self.ldb.get(bytes('b', 'utfM ds = BCDataStream() ds.write(block_data) version = ds.read_var_int() height = ds.read_var_int() status = ds.read_var_int() tx_count = ds.read_var_int() file_num = ds.read_var_int() block_pos_in_file = ds.read_var_int() - 8 block_undo_pos_in_file = ds.read_var_int() block_header = ds.read_bytes(80) self.prepare_data_stream(file_num, block_pos_in_file) block = self.read_block(self.data_stream) block['block_index'] = height return block def read_raw_transaction(self, tx_hash): tx_hash = binascii.unhexlify(inverse_hash(tx_hash)) tx_data = self.ldb.get(bytes('t', 'utf-8') + tx_hash) ds = BCDataStream() ds.write(tx_data) file_num = ds.read_var_int() block_pos_in_file = ds.read_var_int() tx_pos_in_block = ds.read_var_int() tx_pos_in_file = block_pos_in_file + 80 + tx_pos_in_block self.prepare_data_stream(file_nuM transaction = self.read_transaction(self.data_stream) return transaction def close(self): if self.current_block_file: self.current_block_file.close() self.ldb.close() class ChainstateParser(): def __init__(self, leveldb_dir): self.ldb = open_leveldb(leveldb_dir) def get_last_block_hash(self): block_hash = self.ldb.get(bytes('B', 'utf-8')) block_hash = ib2h(block_hash) return block_hash self.ldb.close() class SolvingError(Exception): pass class SerializationError(Exception): """ Thrown when there's a problem deserializing or serializing """ import binascii, os, random, json, hashlib bytes_from_int = chr if bytes == str else lambda x: bytes([x]) return binascii.hexlify(b).decode('utf-8') def random_hex(length): return binascii.b2a_hex(os.urandom(length)) def double_hash(b): return hashlib.sha256(hashliM b.sha256(b).digest()).digest() def inverse_hash(hashstring): hashstring = hashstring[::-1] return ''.join([hashstring[i:i+2][::-1] for i in range(0, len(hashstring), 2)]) return inverse_hash(b2h(b)) class JsonDecimalEncoder(json.JSONEncoder): def default(self, o): if isinstance(o, decimal.Decimal): return str(o) return super(DecimalEncoder, self).default(o) #### p2sh_encoding.py This module contains p2sh data encodingM import traceback # not needed if not printing exceptions on p2sh decoding logger = logging.getLogger(__name__) import bitcoin as bitcoinlib from bitcoin.core.script import CScript from counterpartylib.lib import config from counterpartylib.lib import script from counterpartylib.lib import exceptions def maximum_data_chunk_size(pubkeylength): if pubkeylength >= 0: return bitcoinlib.core.script.MAX_SCRIPT_ELEMENT_SIZE - len(confiM g.PREFIX) - pubkeylength - 12 #Two bytes are for unique offset. This will work for a little more than 1000 outputs return bitcoinlib.core.script.MAX_SCRIPT_ELEMENT_SIZE - len(config.PREFIX) - 44 # Redeemscript size for p2pkh addresses, multisig won't work here def calculate_outputs(destination_outputs, data_array, fee_per_kb, exact_fee=None): datatx_size = 10 # 10 base datatx_size += 181 # 181 for source input datatx_size += (25 + 9) * len(destination_outputs) # destination outputsM datatx_size += 13 # opreturn that signals P2SH encoding datatx_size += len(data_array) * (9 + 181) # size of p2sh inputs, excl data datatx_size += sum([len(data_chunk) for data_chunk in data_array]) # data in scriptSig datatx_necessary_fee = int(datatx_size / 1000 * fee_per_kb) pretx_output_size = 10 # 10 base pretx_output_size += len(data_array) * 29 # size of P2SH output size_for_fee = pretx_output_size # split the tx fee evenly between all datatx outputs = math.ceil(datatx_necessary_fee / len(data_array)) data_value = config.DEFAULT_REGULAR_DUST_SIZE # adjust the data output with the new value and recalculate data_btc_out data_btc_out = data_value * len(data_array) remain_fee = exact_fee - data_value * len(data_array) if remain_fee > 0: #if the dust isn't enough to reach the exact_fee, data value will be an array with only the last fee bumped data_value = [data_value for i in range(len(data_arrM data_value[len(data_array)-1] = data_value[len(data_array)-1] + remain_fee data_btc_out = exact_fee data_output = (data_array, data_value) logger.getChild('p2shdebug').debug('datatx size: %d fee: %d' % (datatx_size, datatx_necessary_fee)) logger.getChild('p2shdebug').debug('pretx output size: %d' % (pretx_output_size, )) logger.getChild('p2shdebug').debug('size_for_fee: %d' % (size_for_fee, )) return size_for_fee, datatx_necessary_fee, data_value, dM def decode_p2sh_input(asm, p2sh_is_segwit=False): ''' Looks at the scriptSig for the input of the p2sh-encoded data transaction [signature] [data] [OP_HASH160 ... OP_EQUAL] pubkey, source, redeem_script_is_valid, found_data = decode_data_redeem_script(asm[-1], p2sh_is_segwit) if redeem_script_is_valid: # this is a signed transaction, so we got {sig[,sig]} {datachunk} {redeemScript} datachunk = found_data redeemScript = asm[-1] #asm[-2:] #print('ASM:', len(asm)) pubkey, source, redeem_script_is_valid, found_data = decode_data_redeem_script(asm[-1], p2sh_is_segwit) if not redeem_script_is_valid or len(asm) != 3: return None, None, None # this is an unsigned transaction (last is outputScript), so we got [datachunk] [redeemScript] [temporaryOutputScript] datachunk, redeemScript, _substituteScript = asm data = datachunk if data[:len(config.PREFIX)] == config.PREFIX: data = data[len(confiM if data == b'': return source, None, None raise exceptions.DecodeError('unrecognised P2SH output') return source, None, data def decode_data_push(arr, pos): opcode = bitcoinlib.core.script.CScriptOp(arr[pos]) if opcode > 0 and opcode < bitcoinlib.core.script.OP_PUSHDATA1: pushlen = arr[pos] elif opcode == bitcoinlib.core.script.OP_PUSHDATA1: pushlen = arr[pos + 1] elif opcode == bitcoinlib.core.script.OP_PUSHDATA2: (pushlen, ) = struct.unpack('<H', arr[pos + 1:pos + 3]) elif opcode == bitcoinlib.core.script.OP_PUSHDATA4: (pushlen, ) = struct.unpack('<L', arr[pos + 1:pos + 5]) return pos + pushlen, arr[pos:pos + pushlen] def decode_data_redeem_script(redeemScript, p2sh_is_segwit=False): script_len = len(redeemScript) found_data = b'' if script_len == 41 and \ redeemScript[0] == bitcoinlib.core.scM redeemScript[35] == bitcoinlib.core.script.OP_CHECKSIGVERIFY and \ redeemScript[37] == bitcoinlib.core.script.OP_DROP and \ redeemScript[38] == bitcoinlib.core.script.OP_DEPTH and \ redeemScript[39] == bitcoinlib.core.script.OP_0 and \ redeemScript[40] == bitcoinlib.core.script.OP_EQUAL: # - OP_DROP [push] [33-byte pubkey] OP_CHECKSIGVERIFY [n] OP_DROP OP_DEPTH 0 OP_EQUAL pubkey = redeemScript[2:35] if p2sh_is_segwit: source = script.pubkey_to_p2whash(pubkey) source = script.pubkey_to_pubkeyhash(pubkey) redeem_script_is_valid = True elif script_len > 41 and \ redeemScript[0] == bitcoinlib.core.script.OP_DROP and \ redeemScript[script_len-4] == bitcoinlib.core.script.OP_DROP and \ redeemScript[script_len-3] == bitcoinlib.core.script.OP_DEPTH and \ redeemScript[script_len-2] == bitcoinlib.core.script.OP_0 and \ redeemScript[scripM t_len-1] == bitcoinlib.core.script.OP_EQUAL: # - OP_DROP {arbitrary multisig script} [n] OP_DROP OP_DEPTH 0 OP_EQUAL pubkey = None source = None redeem_script_is_valid = True pubkey = None source = None redeem_script_is_valid = False opcode = bitcoinlib.core.script.CScriptOp(redeemScript[0]) if opcode > bitcoinlib.core.script.OP_0 and opcode < bitcoinlib.core.script.OP_PUSHDATA1 or \ opcode in (bitcoinlib.core.script.OP_PUSHDATA1, bitcoinlib.core.script.OP_PUSHDATA2, bitcoinlib.core.script.OP_PUSHDATA4): pos = 0 pos, found_data = decode_data_push(redeemScript, 0) if redeemScript[pos] == bitcoinlib.core.script.OP_DROP: pos += 1 valid_sig = False opcode = redeemScript[pos] if type(opcode) != type(''): if opcode >= bitcoinlib.core.script.OP_2 M and opcode <= bitcoinlib.core.script.OP_15: # it's multisig req_sigs = opcode - bitcoinlib.core.script.OP_1 + 1 pos += 1 pubkey = None num_sigs = 0 found_sigs = False while not found_sigs: pos, npubkey = decode_data_push(redeemScript, pos) num_sigs += 1 if redeemScript[pos] - bitcoinlib.core.script.OP_1 + 1 == num_sigs: found_sigs = True pos += 1 valid_sig = redeemScript[pos] == bitcoinlib.core.script.OP_CHECKMULTISIGVERIFY else: # it's p2pkh pos, pubkey = decode_data_push(redeemScript, pos) if p2sh_is_segwit: M source = script.pubkey_to_p2whash(pubkey) else: source = script.pubkey_to_pubkeyhash(pubkey) valid_sig = redeemScript[pos] == bitcoinlib.core.script.OP_CHECKSIGVERIFY pos += 1 if valid_sig: uniqueOffsetLength = 0 for i in range(pos+1, len(redeemScript)): if redeemScript[i] == bitcoinlib.corM uniqueOffsetLength = i-pos-1 break redeem_script_is_valid = redeemScript[pos + 1 + uniqueOffsetLength] == bitcoinlib.core.script.OP_DROP and \ redeemScript[pos + 2 + uniqueOffsetLength] == bitcoinlib.core.script.OP_DEPTH and \ redeemScript[pos + 3 + uniqueOffsetLength] == 0 and \ redeemScript[pos + 4 + uM niqueOffsetLength] == bitcoinlib.core.script.OP_EQUAL except Exception as e: pass #traceback.print_exc() return pubkey, source, redeem_script_is_valid, found_data def make_p2sh_encoding_redeemscript(datachunk, n, pubKey=None, multisig_pubkeys=None, multisig_pubkeys_required=None): _logger = logger.getChild('p2sh_encoding') assert len(datachunk) <= bitcoinlib.core.script.MAX_SCRIPT_ELEMENT_SIZE dataDropScript = [datachunk, bitcoinlib.core.script.OP_DROP] # just drop the data chM cleanupScript = [n, bitcoinlib.core.script.OP_DROP, bitcoinlib.core.script.OP_DEPTH, 0, bitcoinlib.core.script.OP_EQUAL] # unique offset + prevent scriptSig malleability if pubKey is not None: # a p2pkh script looks like this: {pubkey} OP_CHECKSIGVERIFY verifyOwnerScript = [pubKey, bitcoinlib.core.script.OP_CHECKSIGVERIFY] elif multisig_pubkeys_required is not None and multisig_pubkeys: # a 2-of-3 multisig looks like this: # 2 {pubkey1} {pubkey2} {pubkey3} 3 OP_CHEM multisig_pubkeys_required = int(multisig_pubkeys_required) if multisig_pubkeys_required < 2 or multisig_pubkeys_required > 15: raise exceptions.TransactionError('invalid multisig pubkeys value') verifyOwnerScript = [multisig_pubkeys_required] for multisig_pubkey in multisig_pubkeys: verifyOwnerScript.append(multisig_pubkey) verifyOwnerScript = verifyOwnerScript + [len(multisig_pubkeys), bitcoinlib.core.script.OP_CHECKMULTISIGVERIFY] raise exceptions.TransactionError('Either pubKey or multisig pubKeys must be provided') #redeemScript = CScript(datachunk) + CScript(dataDropScript + verifyOwnerScript + cleanupScript) redeemScript = CScript(dataDropScript + verifyOwnerScript + cleanupScript) _logger.debug('datachunk %s' % (binascii.hexlify(datachunk))) _logger.debug('dataDropScript %s (%s)' % (repr(CScript(dataDropScript)), binascii.hexlify(CScript(dataDropScript)))) _logger.debug('verifyOwnerScript %s (%s)' %M (repr(CScript(verifyOwnerScript)), binascii.hexlify(CScript(verifyOwnerScript)))) _logger.debug('entire redeemScript %s (%s)' % (repr(redeemScript), binascii.hexlify(redeemScript))) #scriptSig = CScript([]) + redeemScript # PUSH(datachunk) + redeemScript scriptSig = CScript([redeemScript]) outputScript = redeemScript.to_p2sh_scriptPubKey() _logger.debug('scriptSig %s (%s)' % (repr(scriptSig), binascii.hexlify(scriptSig))) _logger.debug('outputScript %s (%s)' % (repr(outputScript), binascM ii.hexlify(outputScript))) # outputScript looks like OP_HASH160 {{ hash160([redeemScript]) }} OP_EQUALVERIFY # redeemScript looks like OP_DROP {{ pubkey }} OP_CHECKSIGVERIFY {{ n }} OP_DROP OP_DEPTH 0 OP_EQUAL # scriptSig is {{ datachunk }} OP_DROP {{ pubkey }} OP_CHECKSIGVERIFY {{ n }} OP_DROP OP_DEPTH 0 OP_EQUAL return scriptSig, redeemScript, outputScript def make_standard_p2sh_multisig_script(multisig_pubkeys, multisig_pubkeys_required): # a 2-of-3 multisig looks like this: ey1} {pubkey2} {pubkey3} 3 OP_CHECKMULTISIG multisig_pubkeys_required = int(multisig_pubkeys_required) multisig_script = [multisig_pubkeys_required] for multisig_pubkey in multisig_pubkeys: multisig_script.append(multisig_pubkey) multisig_script = multisig_script + [len(multisig_pubkeys), bitcoinlib.core.script.OP_CHECKMULTISIG] return multisig_script Construct and serialize the Bitcoin transactions that are Counterparty transactions. logger = logging.getLogger(__name__) import bitcoin as bitcoinlib from bitcoin.core import Hash160 from bitcoin.core.script import CScript from bitcoin.wallet import P2PKHBitcoinAddress, P2SHBitcoinAddress from counterpartylib.lib import config from counterpartylib.lib import exceptions from counterpartylib.lib import util ounterpartylib.lib import script from counterpartylib.lib import backend from counterpartylib.lib import arc4 from counterpartylib.lib.transaction_helper import p2sh_encoding from bitcoin.bech32 import CBech32Data OP_PUSHDATA1 = b'\x4c' OP_HASH160 = b'\xa9' OP_EQUALVERIFY = b'\x88' OP_CHECKSIG = b'\xac' OP_CHECKMULTISIG = b'\xae' UTXO_LOCKS_PER_ADDREM SS_MAXSIZE = 5000 # set higher than the max number of UTXOs we should expect to # manage in an aging cache for any one source address, at any one period return (i).to_bytes(1, byteorder='little') elif i <= 0xffff: return b'\xfd' + (i).to_bytes(2, byteorder='little') elif i <= 0xffffffff: return b'\xfe' + (i).to_bytes(4, byteorder='little') return b'\xff' + (i).to_bytes(8, byteorder='little') return (i).to_bytes(1, byteorder='little') # Push i bytes. return b'\x4c' + (i).to_bytes(1, byteorder='little') # OP_PUSHDATA1 elif i <= 0xffff: return b'\x4d' + (i).to_bytes(2, byteorder='little') # OP_PUSHDATA2 return b'\x4e' + (i).to_bytes(4, byteorder='little') # OP_PUSHDATA4 def get_script(address): if script.is_multisig(address): return get_multisig_script(address) script.is_bech32(address): return get_p2w_script(address) return get_monosig_script(address) except script.VersionByteError as e: return get_p2sh_script(address) def get_multisig_script(address): signatures_required, pubkeys, signatures_possible = script.extract_array(address) # Required signatures. if signatures_required == 1: op_required = OP_1 elif signatures_required == 2: elif signatures_required == 3: op_required = OP_3 raise script.InputError('Required signatures must be 1, 2 or 3.') # Required signatures. # Note 1-of-1 addresses are not supported (they don't go through extract_array anyway). if signatures_possible == 2: op_total = OP_2 elif signatures_possible == 3: op_total = OP_3 raise script.InputError('Total possible signatures must be 2 or 3.') # Construct script. = op_required # Required signatures for public_key in pubkeys: public_key = binascii.unhexlify(public_key) tx_script += op_push(len(public_key)) # Push bytes of public key tx_script += public_key # Data chunk (fake) public key tx_script += op_total # Total signatures tx_script += OP_CHECKMULTISIG # OP_CHECKMULTISIG return (tx_script, None) et_monosig_script(address): # Construct script. pubkeyhash = script.base58_check_decode(address, config.ADDRESSVERSION) tx_script = OP_DUP # OP_DUP tx_script += OP_HASH160 # OP_HASH160 tx_script += op_push(20) # Push 0x14 bytes tx_script += pubkeyhash # pubKeyHash tx_script += OP_EQUALVERIFY # OP_EQUALVERIFY tx_script += OP_CHECKSM IG # OP_CHECKSIG return (tx_script, None) def get_p2sh_script(address): # Construct script. scripthash = script.base58_check_decode(address, config.P2SH_ADDRESSVERSION) tx_script = OP_HASH160 tx_script += op_push(len(scripthash)) tx_script += scripthash tx_script += OP_EQUAL return (tx_script, None) def get_p2w_script(address): # Construct script. scripthash = bytes(CBech32Data(address)) if len(scripthash) == 20: # P2WPKH encM tx_script = OP_0 tx_script += b'\x14' tx_script += scripthash witness_script = OP_HASH160 witness_script += op_push(len(scripthash)) witness_script += scripthash witness_script += OP_EQUAL return (tx_script, witness_script) elif len(scripthash) == 32: # P2WSH encoding raise Exception('P2WSH encoding not yet supported') def make_fully_valid(pubkey_start): """Take a too short data pubkey and make it look like a real pubM Take an obfuscated chunk of data that is two bytes too short to be a pubkey and add a sign byte to its beginning and a nonce byte to its end. Choose these bytes so that the resulting sequence of bytes is a fully valid pubkey (i.e. on the ECDSA curve). Find the correct bytes by guessing randomly until the check passes. (In parsing, these two bytes are ignored.) assert type(pubkey_start) == bytes assert len(pubkey_start) == 31 # One sign byte and one nonce byte required (fM random_bytes = hashlib.sha256(pubkey_start).digest() # Deterministically generated, for unit tests. sign = (random_bytes[0] & 0b1) + 2 # 0x02 or 0x03 nonce = initial_nonce = random_bytes[1] while not script.is_fully_valid(pubkey): # Increment nonce. assert nonce != initial_nonce # Construct a possibly fully valid public key. pubkey = bytes([sign]) + pubkey_start + bytes([nonce % 256]) rt len(pubkey) == 33 def serialise(encoding, inputs, destination_outputs, data_output=None, change_output=None, dust_return_pubkey=None): s = (1).to_bytes(4, byteorder='little') # Version use_segwit = False for i in range(len(inputs)): txin = inputs[i] spk = txin['scriptPubKey'] if spk[0:2] == '00': # Witness version 0 datalen = binascii.unhexlify(spk[2:4])[0] if datalen == 20 or datalen == 32: # 20 is fM or P2WPKH and 32 is for P2WSH if not(use_segwit): s = (2).to_bytes(4, byteorder='little') # Rewrite version use_segwit = True txin['is_segwit'] = True s += b'\x00' # marker s += b'\x01' # flag # Number of inputs. s += var_int(int(len(inputs))) witness_txins = [] witness_data = {} # List of Inputs. for i in range(len(inputs)): txin = inputs[i] s += binascii.unhexlifM y(bytes(txin['txid'], 'utf-8'))[::-1] # TxOutHash s += txin['vout'].to_bytes(4, byteorder='little') # TxOutIndex tx_script = binascii.unhexlify(bytes(txin['scriptPubKey'], 'utf-8')) s += var_int(int(len(tx_script))) # Script length s += tx_script # Script s += b'\xff' * 4 # Sequence # Number of outputs. n += len(destination_outputs) if data_output:M data_array, value = data_output for data_chunk in data_array: n += 1 data_array = [] if change_output: n += 1 # Destination output. for destination, value in destination_outputs: s += value.to_bytes(8, byteorder='little') # Value tx_script, witness_script = get_script(destination) #if use_segwit and destination in witness_data: # Not deleteing, We will need this for P2WSH # witness_data[destination].appeM # tx_script = witness_script #if witness_script: # tx_script = witness_script s += var_int(int(len(tx_script))) # Script length s += tx_script for data_chunk in data_array: data_array, value = data_output s += value.to_bytes(8, byteorder='little') # Value data_chunk = config.PREFIX + data_chunk # Initialise encryption key (once per output). assert isinstancM e(inputs[0]['txid'], str) key = arc4.init_arc4(binascii.unhexlify(inputs[0]['txid'])) # Arbitrary, easy if encoding == 'multisig': assert dust_return_pubkey # Get data (fake) public key. pad_length = (33 * 2) - 1 - 2 - 2 - len(data_chunk) assert pad_length >= 0 data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00') data_chunk = key.encrypt(data_chunk) data_pubkey_1 = maM ke_fully_valid(data_chunk[:31]) data_pubkey_2 = make_fully_valid(data_chunk[31:]) # Construct script. tx_script = OP_1 # OP_1 tx_script += op_push(33) # Push bytes of data chunk (fake) public key (1/2) tx_script += data_pubkey_1 # (Fake) public key (1/2) tx_script += op_push(33) # Push bytes of data chunk (fake) pubM tx_script += data_pubkey_2 # (Fake) public key (2/2) tx_script += op_push(len(dust_return_pubkey)) # Push bytes of source public key tx_script += dust_return_pubkey # Source public key tx_script += OP_3 # OP_3 tx_script += OP_CHECKMULTISIG # OP_CHECKMULTISIG elif encoding == 'opreturn': data_chunk = key.M tx_script = OP_RETURN # OP_RETURN tx_script += op_push(len(data_chunk)) # Push bytes of data chunk (NOTE: OP_SMALLDATA?) tx_script += data_chunk # Data elif encoding == 'pubkeyhash': pad_length = 20 - 1 - len(data_chunk) assert pad_length >= 0 data_chunk = bytes([len(data_chunk)]) + data_chunk + (pad_length * b'\x00') data_chM unk = key.encrypt(data_chunk) # Construct script. tx_script = OP_DUP # OP_DUP tx_script += OP_HASH160 # OP_HASH160 tx_script += op_push(20) # Push 0x14 bytes tx_script += data_chunk # (Fake) pubKeyHash tx_script += OP_EQUALVERIFY # OP_EQUALVERIFY tx_script += OP_CHECKSIG M # OP_CHECKSIG raise exceptions.TransactionError('Unknown encoding s += var_int(int(len(tx_script))) # Script length s += tx_script # Change output. if change_output: change_address, change_value = change_output s += change_value.to_bytes(8, byteorder='little') # Value tx_script, witness_script = get_script(change_address) #print("Change address!", change_address, "\n", witnessM _data, "\n", tx_script, "\n", witness_script) #if witness_script: #use_segwit and change_address in witness_data: # if not(change_address in witness_data): # witness_data[change_address] = [] # witness_data[change_address].append(witness_script) # tx_script = witness_script # use_segwit = True s += var_int(int(len(tx_script))) # Script length s += tx_script for i in range(len(inputs))M txin = inputs[i] if txin['is_segwit']: s += b'\x02' s += b'\x00\x00' s += b'\x00' s += (0).to_bytes(4, byteorder='little') # LockTime def serialise_p2sh_pretx(inputs, source, source_value, data_output, change_output=None, pubkey=None, multisig_pubkeys=None, multisig_pubkeys_required=None): assert data_output # we don't do this unless there's data data_array, data_value = data_outM s = (1).to_bytes(4, byteorder='little') # Version # Number of inputs. s += var_int(int(len(inputs))) # List of Inputs. for i in range(len(inputs)): txin = inputs[i] s += binascii.unhexlify(bytes(txin['txid'], 'utf-8'))[::-1] # TxOutHash s += txin['vout'].to_bytes(4, byteorder='little') # TxOutIndex tx_script = binascii.unhexlify(bytes(txin['scriptPubKey'], 'utf-8')) s += var_int(int(len(tx_script))) # Script length ript # Script s += b'\xff' * 4 # Sequence # Number of outputs. n = len(data_array) if change_output: # encode number of outputs # P2SH for data encodeded inputs for n, data_chunk in enumerate(data_array): data_chunk = config.PREFIX + data_chunk # prefix the data_chunk # get the scripts scriptSig, redeemScript, outputScript = p2sh_encoding.make_p2sh_encoding_redeemscript(data_chunk, nM , pubkey, multisig_pubkeys, multisig_pubkeys_required) #if data_value is an array, then every output fee is specified in it if type(data_value) == list: s += data_value[n].to_bytes(8, byteorder='little') # Value s += data_value.to_bytes(8, byteorder='little') # Value s += var_int(int(len(outputScript))) # Script length s += outputScript # Script # Change output. if change_output: ge_address, change_value = change_output tx_script, witness_script = get_script(change_address) s += change_value.to_bytes(8, byteorder='little') # Value s += var_int(int(len(tx_script))) # Script length s += tx_script # Script s += (0).to_bytes(4, byteorder='little') # LockTime def serialise_p2sh_datatx(txid, source, source_input, destination_outputs, data_output, pubkey=None, multisig_pubkeys=None, multisiM g_pubkeys_required=None): assert data_output # we don't do this unless there's data txhash = bitcoinlib.core.lx(bitcoinlib.core.b2x(txid)) # reverse txId data_array, value = data_output s = (1).to_bytes(4, byteorder='little') # number of inputs is the length of data_array (+1 if a source_input exists) number_of_inputs = len(data_array) if source_input is not None: number_of_inputs += 1 s += var_int(number_of_inputs) # Handle a source input here for a PM if source_input is not None: s += binascii.unhexlify(bytes(source_input['txid'], 'utf-8'))[::-1] # TxOutHash s += source_input['vout'].to_bytes(4, byteorder='little') # TxOutIndex # since pubkey is not returned from indexd, add it from bitcoind source_inputs = backend.ensure_script_pub_key_for_inputs([source_input]) source_input = source_inputs[0] tx_script = binascii.unhexlify(bytes(source_input['scriptPubKey'], 'utf-8')) int(int(len(tx_script))) # Script length s += tx_script # Script s += b'\xff' * 4 # Sequence # list of inputs for n, data_chunk in enumerate(data_array): data_chunk = config.PREFIX + data_chunk # prefix the data_chunk # get the scripts scriptSig, redeemScript, outputScript = p2sh_encoding.make_p2sh_encoding_redeemscript(data_cM hunk, n, pubkey, multisig_pubkeys, multisig_pubkeys_required) #substituteScript = scriptSig + outputScript s += txhash # TxOutHash s += (n).to_bytes(4, byteorder='little') # TxOutIndex (assumes 0-based) #s += var_int(len(substituteScript)) # Script length #s += substituteScript # Script s += var_int(len(scriptSig))# + len(outputScript)) M # Script length s += scriptSig # Script #s += outputScript # Script s += b'\xff' * 4 # Sequence # number of outputs, always 1 for the opreturn n += len(destination_outputs) # encode output length # destination outputs for destination, value in destination_outputs: tx_script, witness_script = get_script(destination)M s += value.to_bytes(8, byteorder='little') # Value s += var_int(int(len(tx_script))) # Script length s += tx_script # Script # opreturn to signal P2SH encoding key = arc4.init_arc4(txid) data_chunk = config.PREFIX + b'P2SH' data_chunk = key.encrypt(data_chunk) tx_script = OP_RETURN # OP_RETURN tx_script += op_push(len(data_chunk)) # Push bytes of data chunk tx_script += data_chunk # Data s += (0).to_bytes(8, byteorder='little') # Value s += var_int(int(len(tx_script))) # Script length s += tx_script # Script s += (0).to_bytes(4, byteorder='little') # LockTime UUUUUUUUUUUUUUUUUUUUUUUUUUUUU !22222222222222222222222222222222222222222222222222 text/plain;charset=utf-8 # Merged Python Files Datastreams are identified by the address that publishes them, and referenced in transaction outputs. For CFD leverage, 1x = 5040, 2x = 10080, etc.: 5040 is a superior highly composite number and a colossally abundant number, and has 1-10, 12 as factors. All wagers are in XCP. Expiring a bet match doesn open the constituent bets. (So all bets may be logger = logging.getLogger(__name__) from counterpartylib.lib import config from counterpartylib.lib import exceptions from counterpartylib.lib import util from counterpartylib.lib import log from counterpartylib.lib import message_type LENGTH = 2 + 4 + 8 + 8 + 8 + 4 + 4 def initialise (db): cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS bets( tx_index INTEGER UNIQUE, tx_hash TEXT M block_index INTEGER, source TEXT, feed_address TEXT, bet_type INTEGER, deadline INTEGER, wager_quantity INTEGER, wager_remaining INTEGER, counterwager_quantity INTEGER, counterwager_remaining INTEGER, target_value REAL, leverage INTEGER, expirationM expire_index INTEGER, fee_fraction_int INTEGER, status TEXT, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index), PRIMARY KEY (tx_index, tx_hash)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON bets (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTSM index_hash_idx ON bets (tx_index, tx_hash) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS expire_idx ON bets (status, expire_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS feed_valid_bettype_idx ON bets (feed_address, status, bet_type) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON bets (source) ''') execute('''CREATE INDEX IF NOT EXISTS status_idx ON bets (status) ''') cursor.execute('''CREATE TABLE IF NOT EXISTS bet_matches( id TEXT PRIMARY KEY, tx0_index INTEGER, tx0_hash TEXT, tx0_address TEXT, tx1_index INTEGER, tx1_hash TEXT, tx1_address TEXT, tx0_bet_type INTEGER, tx1_bet_type INTEGER, feed_address TEXT, initial_value INTEGER, deadline INTEGER, target_value REAL, leverage INTEGER, forward_quantity INTEGER, backward_quantity INTEGER, tx0_block_index INTEGER, tx1_block_index INTEGER, block_index INTEGER, tx0_expiration INM tx1_expiration INTEGER, match_expire_index INTEGER, fee_fraction_int INTEGER, status TEXT, FOREIGN KEY (tx0_index, tx0_hash, tx0_block_index) REFERENCES transactions(tx_index, tx_hash, block_index), FOREIGN KEY (tx1_index, tx1_hash, tx1_block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS match_expire_idx ON bet_matches (status, match_expire_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS valid_feed_idx ON bet_matches (feed_address, status) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS id_idx ON bet_matches (id) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx0_address_idx ON bet_matches (tx0_address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx1_address_idx ON bet_matches (tx1_address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS status_idx ON bet_matches (status) ''') # Bet Expirations cursor.execute('''CREATE TABLE IF NOT EXISTS bet_expirations( bet_index INTEGER PRIMARY KEY, bet_hash TEXT UNIQUE, source TEXT, block_indexM FOREIGN KEY (block_index) REFERENCES blocks(block_index), FOREIGN KEY (bet_index, bet_hash) REFERENCES bets(tx_index, tx_hash)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON bet_expirations (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON bet_expirations (source) ''') # Bet Match Expirations sor.execute('''CREATE TABLE IF NOT EXISTS bet_match_expirations( bet_match_id TEXT PRIMARY KEY, tx0_address TEXT, tx1_address TEXT, block_index INTEGER, FOREIGN KEY (bet_match_id) REFERENCES bet_matches(id), FOREIGN KEY (block_index) REFERENCES blocks(block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON bet_matcM h_expirations (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx0_address_idx ON bet_match_expirations (tx0_address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx1_address_idx ON bet_match_expirations (tx1_address) ''') # Bet Match Resolutions cursor.execute('''CREATE TABLE IF NOT EXISTS bet_match_resolutions( bet_match_id TEXT PRIMARY KEY, bet_match_type_id INTEGER, block_index INTEGER, winner TEXT, settled BOOL, bull_credit INTEGER, bear_credit INTEGER, escrow_less_fee INTEGER, fee INTEGER, FOREIGN KEY (bet_match_id) REFERENCES bet_matches(id), FOREIGN KEY (block_index) REFERENCES blocks(block_index)) ''') def cancel_bet (db, bM et, status, block_index): cursor = db.cursor() # Update status of bet. 'status': status, 'tx_hash': bet['tx_hash'] sql='update bets set status = :status where tx_hash = :tx_hash' cursor.execute(sql, bindings) log.message(db, block_index, 'update', 'bets', bindings) util.credit(db, bet['source'], config.XCP, bet['wager_remaining'], action='recredit wager remaining', event=bet['tx_hash']) cursor = db.cursor() def cancel_bet_match (db, bet_match, sM tatus, block_index): fill, etc. constituent bets. cursor = db.cursor() # Recredit tx0 address. util.credit(db, bet_match['tx0_address'], config.XCP, bet_match['forward_quantity'], action='recredit forward quantity', event=bet_match['id']) # Recredit tx1 address. util.credit(db, bet_match['tx1_address'], config.XCP, bet_match['backward_quantity'], action='recredit backward quantity', event=bet_match['id']) # Update status of bM 'status': status, 'bet_match_id': bet_match['id'] sql='update bet_matches set status = :status where id = :bet_match_id' cursor.execute(sql, bindings) log.message(db, block_index, 'update', 'bet_matches', bindings) def get_fee_fraction (db, feed_address): '''Get fee fraction from last broadcast from the feed_address address. cursor = db.cursor() broadcasts = list(cursor.execute('''SELECT * FROM broadcasts WHERE (sM tatus = ? AND source = ?) ORDER BY tx_index ASC''', ('valid', feed_address))) last_broadcast = broadcasts[-1] fee_fraction_int = last_broadcast['fee_fraction_int'] if fee_fraction_int: return fee_fraction_int / 1e8 else: return 0 def validate (db, source, feed_address, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration, block_index): erage is None: leverage = 5040 if wager_quantity > config.MAX_INT or counterwager_quantity > config.MAX_INT or bet_type > config.MAX_INT \ or deadline > config.MAX_INT or leverage > config.MAX_INT or block_index + expiration > config.MAX_INT: problems.append('integer overflow') # Look at feed to be bet on. cursor = db.cursor() broadcasts = list(cursor.execute('''SELECT * FROM broadcasts WHERE (status = ? AND source = ?) ORDER BY tx_index ASC''', ('valid', feedM if not broadcasts: problems.append('feed doesn elif not broadcasts[-1]['text']: problems.append('feed is locked') elif broadcasts[-1]['timestamp'] >= deadline: problems.append('deadline in that feed if not bet_type in (0, 1, 2, 3): problems.append('unknown bet type') # Valid leverage level? if leverage != 5040 and bet_type in (2,3): # Equal, NotEqual problems.append('leverage used with Equal or M if leverage < 5040 and not bet_type in (0,1): # BullCFD, BearCFD (fractional leverage makes sense precisely with CFDs) problems.append('leverage level too low') if bet_type in (0,1): # BullCFD, BearCFD if block_index >= 312350: # Protocol change. problems.append('CFDs temporarily disabled') if not isinstance(wager_quantity, int): problems.append('wager_quantity must be in satoshis') return problems, leverage if not isinstance(counterwageM problems.append('counterwager_quantity must be in satoshis') return problems, leverage if not isinstance(expiration, int): problems.append('expiration must be expressed as an integer block delta') return problems, leverage if wager_quantity <= 0: problems.append('non if counterwager_quantity <= 0: problems.append('non positive counterwager') if deadline < 0: problems.append('negative deadline') if expiration < 0: problems.apM pend('negative expiration') if expiration == 0 and not (block_index >= 317500 or config.TESTNET or config.REGTEST): # Protocol change. problems.append('zero expiration') if target_value: if bet_type in (0,1): # BullCFD, BearCFD problems.append('CFDs have no target value') if target_value < 0: problems.append('negative target value') if expiration > config.MAX_EXPIRATION: problems.append('expiration overflow') return problems, leverage ef compose (db, source, feed_address, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration): if util.get_balance(db, source, config.XCP) < wager_quantity: raise exceptions.ComposeError('insufficient funds') problems, leverage = validate(db, source, feed_address, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration, util.CURRENT_BLOCK_INDEX) if util.date_passed(deadline): problems.append('deadline passed') if problems: raise exceptions.ComposeError(problems) data = message_type.pack(ID) data += struct.pack(FORMAT, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration) return (source, [(feed_address, None)], data) def parse (db, tx, message): bet_parse_cursor = db.cursor() # Unpack message. if len(message) != LENGTH: raise exceptions.UnpackM (bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration) = struct.unpack(FORMAT, message) status = 'open' except (exceptions.UnpackError, struct.error): (bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration, fee_fraction_int) = 0, 0, 0, 0, 0, 0, 0, 0 status = 'invalid: could not unpack' odds, fee_fraction = 0, 0 feed_address = tx['destination'] odds = util.price(wager_quantity, counterwager_quantity) except ZeroDivisionError: odds = 0 fee_fraction = get_fee_fraction(db, feed_address) bet_parse_cursor.execute('''SELECT * FROM balances \ WHERE (address = ? AND asset = ?)''', (tx['source'], config.XCP)) balances = list(bet_parse_cursor) if not balances: wager_quantity = 0 balance = balances[0]['quantity'] if balance < wager_quantity: wager_quantity = balance counterwager_quantity = int(util.price(wager_quantity, odds)) problems, leverage = validate(db, tx['source'], feed_address, bet_type, deadline, wager_quantity, counterwager_quantity, target_value, leverage, expiration, tx['block_index']) if problems: status = 'invalid: ' + '; '.join(problems) # Debit quantity wagered. (Escrow.) util.debit(db, tx['source'], config.XCP, wager_quantity, action='bet', event=tx['tx_hash']) # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'feed_address': feed_address, 'bet_type': bet_type, 'deadline': deadline, 'wager_quantity': wager_quantity, 'wager_remaining': wager_quanM 'counterwager_quantity': counterwager_quantity, 'counterwager_remaining': counterwager_quantity, 'target_value': target_value, 'leverage': leverage, 'expiration': expiration, 'expire_index': tx['block_index'] + expiration, 'fee_fraction_int': fee_fraction * 1e8, 'status': status, if "integer overflow" not in status: sql = 'insert into bets values(:tx_index, :tx_hash, :block_index, :source, :feed_address, :bet_type, :deadline, :wM ager_quantity, :wager_remaining, :counterwager_quantity, :counterwager_remaining, :target_value, :leverage, :expiration, :expire_index, :fee_fraction_int, :status)' bet_parse_cursor.execute(sql, bindings) logger.warn("Not storing [bet] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(bindings), )) if status == 'open' and tx['block_index'] != config.MEMPOOL_BLOCK_INDEX: match(db, tx) bet_parse_cursor.close() cursor = db.cursor() # Get bet in question. bets = list(cursor.execute('''SELECT * FROM bets\ WHERE (tx_index = ? AND status = ?)''', (tx['tx_index'], 'open'))) cursor.close() assert len(bets) == 1 # Get counterbet_type. if tx1['bet_type'] % 2: counterbet_type = tx1['bet_type'] - 1 else: counterbet_type = tx1['bet_type'] + 1 feed_address = tx1['feed_address'] ursor.execute('''SELECT * FROM bets\ WHERE (feed_address=? AND status=? AND bet_type=?)''', (tx1['feed_address'], 'open', counterbet_type)) tx1_wager_remaining = tx1['wager_remaining'] tx1_counterwager_remaining = tx1['counterwager_remaining'] bet_matches = cursor.fetchall() if tx['block_index'] > 284500 or config.TESTNET or config.REGTEST: # Protocol change. sorted(bet_matches, key=lambda x: x['tx_index']) M # Sort by tx index second. sorted(bet_matches, key=lambda x: util.price(x['wager_quantity'], x['counterwager_quantity'])) # Sort by price first. tx1_status = tx1['status'] for tx0 in bet_matches: if tx1_status != 'open': break logger.debug('Considering: ' + tx0['tx_hash']) tx0_wager_remaining = tx0['wager_remaining'] tx0_counterwager_remaining = tx0['counterwager_remaining'] # Bet types must be opposite. if counterbet_type != tx0['betM logger.debug('Skipping: bet types disagree.') continue # Leverages must agree exactly if tx0['leverage'] != tx1['leverage']: logger.debug('Skipping: leverages disagree.') continue # Target values must agree exactly. if tx0['target_value'] != tx1['target_value']: logger.debug('Skipping: target values disagree.') continue # Fee fractions must agree exactly. if tx0['fee_fraction_int'] != M tx1['fee_fraction_int']: logger.debug('Skipping: fee fractions disagree.') continue # Deadlines must agree exactly. if tx0['deadline'] != tx1['deadline']: logger.debug('Skipping: deadlines disagree.') continue # If the odds agree, make the trade. The found order sets the odds, # and they trade as much as they can. tx0_odds = util.price(tx0['wager_quantity'], tx0['counterwager_quantity']) tx0_inverse_odds = util.price(tM x0['counterwager_quantity'], tx0['wager_quantity']) tx1_odds = util.price(tx1['wager_quantity'], tx1['counterwager_quantity']) if tx['block_index'] < 286000: tx0_inverse_odds = util.price(1, tx0_odds) # Protocol change. logger.debug('Tx0 Inverse Odds: {}; Tx1 Odds: {}'.format(float(tx0_inverse_odds), float(tx1_odds))) if tx0_inverse_odds > tx1_odds: logger.debug('Skipping: price mismatch.') logger.debug('Potential forward quantities: {}, {}'.foM rmat(tx0_wager_remaining, int(util.price(tx1_wager_remaining, tx1_odds)))) forward_quantity = int(min(tx0_wager_remaining, int(util.price(tx1_wager_remaining, tx1_odds)))) logger.debug('Forward Quantity: {}'.format(forward_quantity)) backward_quantity = round(forward_quantity / tx0_odds) logger.debug('Backward Quantity: {}'.format(backward_quantity)) if not forward_quantity: logger.debug('Skipping: zero forward quantity.') if tx1['block_index'] >= 286500 or config.TESTNET or config.REGTEST: # Protocol change. if not backward_quantity: logger.debug('Skipping: zero backward quantity.') continue bet_match_id = util.make_id(tx0['tx_hash'], tx1['tx_hash']) # Debit the order. # Counterwager remainings may be negative. tx0_wager_remaining = tx0_wager_remaining - forward_quantity tx0_counterwager_remaiM ning = tx0_counterwager_remaining - backward_quantity tx1_wager_remaining = tx1_wager_remaining - backward_quantity tx1_counterwager_remaining = tx1_counterwager_remaining - forward_quantity tx0_status = 'open' if tx0_wager_remaining <= 0 or tx0_counterwager_remaining <= 0: # Fill order, and recredit give_remaining. tx0_status = 'filled' util.credit(db, tx0['source'], config.XCP, tx0_wager_remaining, eM vent=tx1['tx_hash'], action='filled') bindings = { 'wager_remaining': tx0_wager_remaining, 'counterwager_remaining': tx0_counterwager_remaining, 'status': tx0_status, 'tx_hash': tx0['tx_hash'] sql='update bets set wager_remaining = :wager_remaining, counterwager_remaining = :counterwager_remaining, status = :status where tx_hash = :tx_hash' cursor.execute(sql, bindings) log.message(db, tx['bM lock_index'], 'update', 'bets', bindings) if tx1['block_index'] >= 292000 or config.TESTNET or config.REGTEST: # Protocol change if tx1_wager_remaining <= 0 or tx1_counterwager_remaining <= 0: # Fill order, and recredit give_remaining. tx1_status = 'filled' util.credit(db, tx1['source'], config.XCP, tx1_wager_remaining, event=tx1['tx_hash'], action='filled') bindings = { 'wager_remM aining': tx1_wager_remaining, 'counterwager_remaining': tx1_counterwager_remaining, 'status': tx1_status, 'tx_hash': tx1['tx_hash'] sql='update bets set wager_remaining = :wager_remaining, counterwager_remaining = :counterwager_remaining, status = :status where tx_hash = :tx_hash' cursor.execute(sql, bindings) log.message(db, tx['block_index'], 'update', 'bets', bindings) # Get last value of feed. broadcasts = list(cursor.execute('''SELECT * FROM broadcasts WHERE (status = ? AND source = ?) ORDER BY tx_index ASC''', ('valid', feed_address))) initial_value = broadcasts[-1]['value'] # Record bet fulfillment. bindings = { 'id': util.make_id(tx0['tx_hash'], tx['tx_hash']), 'tx0_index': tx0['tx_index'], 'tx0_hash': tx0['tx_hash'], 'tx0_address': tx0['source'], 'tx1_index': tx1['tx_index'], 'tx1_hash': tx1['tx_hash'], 'tx1_address': tx1['source'], 'tx0_bet_type': tx0['bet_type'], 'tx1_bet_type': tx1['bet_type'], 'feed_address': tx1['feed_address'], 'initial_value': initial_value, 'deadline': tx1['deadline'], 'target_value': tx1['target_value'], 'leverage': tx1['leverage'], 'forward_quantity': forward_quantity, 'backward_quantity': bM 'tx0_block_index': tx0['block_index'], 'tx1_block_index': tx1['block_index'], 'block_index': max(tx0['block_index'], tx1['block_index']), 'tx0_expiration': tx0['expiration'], 'tx1_expiration': tx1['expiration'], 'match_expire_index': min(tx0['expire_index'], tx1['expire_index']), 'fee_fraction_int': tx1['fee_fraction_int'], 'status': 'pending', ql='insert into bet_matches values(:id, :tx0_index, :tx0_hash, :tx0_address, :tx1_index, :tx1_hash, :tx1_address, :tx0_bet_type, :tx1_bet_type, :feed_address, :initial_value, :deadline, :target_value, :leverage, :forward_quantity, :backward_quantity, :tx0_block_index, :tx1_block_index, :block_index, :tx0_expiration, :tx1_expiration, :match_expire_index, :fee_fraction_int, :status)' cursor.execute(sql, bindings) def expire (db, block_index, block_time): cursor = db.curM # Expire bets and give refunds for the quantity wager_remaining. cursor.execute('''SELECT * FROM bets \ WHERE (status = ? AND expire_index < ?)''', ('open', block_index)) for bet in cursor.fetchall(): cancel_bet(db, bet, 'expired', block_index) # Record bet expiration. bindings = { 'bet_index': bet['tx_index'], 'bet_hash': bet['tx_hash'], 'source': bet['source'], 'block_index': block_index sql='insert into bet_expirations values(:bet_index, :bet_hash, :source, :block_index)' cursor.execute(sql, bindings) # Expire bet matches whose deadline is more than two weeks before the current block time. cursor.execute('''SELECT * FROM bet_matches \ WHERE (status = ? AND deadline < ?)''', ('pending', block_time - config.TWO_WEEKS)) for bet_match in cursor.fetchall(): cancel_bet_match(db, bet_match, 'expired', block_index) # Record bet match expiratM bindings = { 'bet_match_id': bet_match['id'], 'tx0_address': bet_match['tx0_address'], 'tx1_address': bet_match['tx1_address'], 'block_index': block_index sql='insert into bet_match_expirations values(:bet_match_id, :tx0_address, :tx1_address, :block_index)' cursor.execute(sql, bindings) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 sage, with or without a price. Multiple messages per block are allowed. Bets are be made on the 'timestamp' field, and not the block index. An address is a feed of broadcasts. Feeds may be locked with a broadcast whose text field is identical to (case insensitive). Bets on a feed reference the address that is the source of the feed in an output which includes the (latest) required fee. Broadcasts without a price may not be used for betting. Broadcasts about events with a small number of possible outcoM mes (e.g. sports games), should be written, for example, such that a price of 1 XCP means one outcome, 2 XCP means another, etc., which schema should be described in the 'text' field. fee_fraction: .05 XCP means 5%. It may be greater than 1, however; but because it is stored as a four byte integer, it may not be greater than about from fractions import Fraction logger = logging.getLogger(__name__) from bitcoin.core import VarInM from counterpartylib.lib import exceptions from counterpartylib.lib import config from counterpartylib.lib import util from counterpartylib.lib import log from counterpartylib.lib import message_type # NOTE: Pascal strings are used for storing texts for backwards cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS broadcasts( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, timestamp INTEGER, value REAL, fee_fraction_int INTEGER, text TEXT, locked BOOL, status TEXT, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATEM INDEX IF NOT EXISTS block_index_idx ON broadcasts (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS status_source_idx ON broadcasts (status, source) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS status_source_index_idx ON broadcasts (status, source, tx_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS timestamp_idx ON broadcasts (timesM ''') def validate (db, source, timestamp, value, fee_fraction_int, text, block_index): if timestamp > config.MAX_INT or value > config.MAX_INT or fee_fraction_int > config.MAX_INT: problems.append('integer overflow') if util.enabled('max_fee_fraction'): if fee_fraction_int >= config.UNIT: problems.append('fee fraction greater than or equal to 1') if fee_fraction_int > 4294967295: problemM s.append('fee fraction greater than 42.94967295') if timestamp < 0: problems.append('negative timestamp') problems.append('null source address') # Check previous broadcast in this feed. cursor = db.cursor() broadcasts = list(cursor.execute('''SELECT * FROM broadcasts WHERE (status = ? AND source = ?) ORDER BY tx_index ASC''', ('valid', source))) last_broadcast = broadcasts[-1] if last_broadcast['locked']: problems.append('locked feed') elif timestamp <= last_broadcast['timestamp']: problems.append('feed timestamps not monotonically increasing') if not (block_index >= 317500 or config.TESTNET or config.REGTEST): # Protocol change. if len(text) > 52: problems.append('text too long') if util.enabled('options_require_memo') and text and text.lower().startswith('options'): # Check for options and if they are valid. options = util.parsM e_options_from_string(text) if options is not False: util.validate_address_options(options) except exceptions.OptionsError as e: problems.append(str(e)) def compose (db, source, timestamp, value, fee_fraction, text): # Store the fee fraction as an integer. fee_fraction_int = int(fee_fraction * 1e8) problems = validate(db, source, timestamp, value, fee_fraction_int, text, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.CoM mposeError(problems) data = message_type.pack(ID) # always use custom length byte instead of problematic usage of 52p format and make sure to encode('utf-8') for length if util.enabled('broadcast_pack_text'): data += struct.pack(FORMAT, timestamp, value, fee_fraction_int) data += VarIntSerializer.serialize(len(text.encode('utf-8'))) data += text.encode('utf-8') if len(text) <= 52: curr_format = FORMAT + '{}p'.format(len(text) + 1) curr_format = FORMAT + '{}s'.format(len(text)) data += struct.pack(curr_format, timestamp, value, fee_fraction_int, text.encode('utf-8')) return (source, [], data) def parse (db, tx, message): cursor = db.cursor() # Unpack message. if util.enabled('broadcast_pack_text', tx['block_index']): timestamp, value, fee_fraction_int, rawtext = struct.unpack(FORMAT + '{}s'.format(len(message) - LENGTH), message) textlen = VarIntSerializer.deserialize(raM if textlen == 0: text = b'' text = rawtext[-textlen:] assert len(text) == textlen if len(message) - LENGTH <= 52: curr_format = FORMAT + '{}p'.format(len(message) - LENGTH) curr_format = FORMAT + '{}s'.format(len(message) - LENGTH) timestamp, value, fee_fraction_int, text = struct.unpack(curr_format, message) text = text.M except UnicodeDecodeError: text = '' status = 'valid' except (struct.error) as e: timestamp, value, fee_fraction_int, text = 0, None, 0, None status = 'invalid: could not unpack' if status == 'valid': # For SQLite3 timestamp = min(timestamp, config.MAX_INT) value = min(value, config.MAX_INT) problems = validate(db, tx['source'], timestamp, value, fee_fraction_int, text, tx['block_index']) if problems: statuM s = 'invalid: ' + '; '.join(problems) if text and text.lower() == 'lock': timestamp, value, fee_fraction_int, text = 0, None, None, None lock = False # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'timestamp': timestamp, 'value': value, fee_fraction_int': fee_fraction_int, 'text': text, 'locked': lock, 'status': status, if "integer overflow" not in status: sql = 'insert into broadcasts values(:tx_index, :tx_hash, :block_index, :source, :timestamp, :value, :fee_fraction_int, :text, :locked, :status)' cursor.execute(sql, bindings) logger.warn("Not storing [broadcast] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(bindings), )) rocessing if broadcast is invalid for any reason if util.enabled('broadcast_invalid_check') and status != 'valid': # Options? Should not fail to parse due to above checks. if util.enabled('options_require_memo') and text and text.lower().startswith('options'): options = util.parse_options_from_string(text) if options is not False: op_bindings = { 'block_index': tx['block_index'], 'address': tx['source'], 'options': options } sql = 'insert or replace into addresses(address, options, block_index) values(:address, :options, :block_index)' cursor = db.cursor() cursor.execute(sql, op_bindings) # Negative values (default to ignore). if value is None or value < 0: # Cancel Open Bets? if value == -2: cursor.execute('''SELECT * FROM bets \ WHERE (status = ? AND feed_address = ?)''', ('open', tx['source'])) for i in list(cursor): bet.cancel_bet(db, i, 'dropped', tx['block_index']) # Cancel Pending Bet Matches? if value == -3: cursor.execute('''SELECT * FROM bet_matches \ WHERE (status = ? AND feed_address = ?)''', ('pending', tx['source'])) for bet_match in list(cursor): bet.cancel_bet_match(db, bet_match, 'dropped', tx['block_indM cursor.close() # stop processing if broadcast is invalid for any reason # @TODO: remove this check once broadcast_invalid_check has been activated if util.enabled('max_fee_fraction') and status != 'valid': # Handle bet matches that use this feed. cursor.execute('''SELECT * FROM bet_matches \ WHERE (status=? AND feed_address=?) ORDER BY tx1_index ASC, tx0_index ASC''', ('pending', tx['sourM for bet_match in cursor.fetchall(): broadcast_bet_match_cursor = db.cursor() bet_match_id = util.make_id(bet_match['tx0_hash'], bet_match['tx1_hash']) bet_match_status = None # Calculate total funds held in escrow and total fee to be paid if # the bet match is settled. Escrow less fee is amount to be paid back # to betters. total_escrow = bet_match['forward_quantity'] + bet_match['backward_quantity'] if util.enabled('inmutable_fee_fractionM fee_fraction = bet_match['fee_fraction_int'] / config.UNIT fee_fraction = fee_fraction_int / config.UNIT fee = int(fee_fraction * total_escrow) # Truncate. escrow_less_fee = total_escrow - fee # Get known bet match type IDs. cfd_type_id = util.BET_TYPE_ID['BullCFD'] + util.BET_TYPE_ID['BearCFD'] equal_type_id = util.BET_TYPE_ID['Equal'] + util.BET_TYPE_ID['NotEqual'] # Get the bet match type ID of this bet matcM bet_match_type_id = bet_match['tx0_bet_type'] + bet_match['tx1_bet_type'] # Contract for difference, with determinate settlement date. if bet_match_type_id == cfd_type_id: # Recognise tx0, tx1 as the bull, bear (in the right direction). if bet_match['tx0_bet_type'] < bet_match['tx1_bet_type']: bull_address = bet_match['tx0_address'] bear_address = bet_match['tx1_address'] bull_escrow = bet_match['forward_quantity'] bear_escrow = bet_match['backward_quantity'] bull_address = bet_match['tx1_address'] bear_address = bet_match['tx0_address'] bull_escrow = bet_match['backward_quantity'] bear_escrow = bet_match['forward_quantity'] leverage = Fraction(bet_match['leverage'], 5040) initial_value = bet_match['initial_value'] bear_credit = bear_escrow - (value - initial_value) * leverage * config.UNIT bull_credit = escrow_less_fee - bear_credit bear_credit = round(bear_credit) bull_credit = round(bull_credit) # Liquidate, as necessary. if bull_credit >= escrow_less_fee or bull_credit <= 0: if bull_credit >= escrow_less_fee: bull_credit = escrow_less_fee bear_credit = 0 bet_match_status = 'settled: liquidated for bull' util.credit(db, bull_address, config.XCP,M bull_credit, action='bet {}'.format(bet_match_status), event=tx['tx_hash']) elif bull_credit <= 0: bull_credit = 0 bear_credit = escrow_less_fee bet_match_status = 'settled: liquidated for bear' util.credit(db, bear_address, config.XCP, bear_credit, action='bet {}'.format(bet_match_status), event=tx['tx_hash']) # Pay fee to feed. util.credit(db, bet_match['feed_address'], config.XCP, feeM , action='feed fee', event=tx['tx_hash']) # For logging purposes. bindings = { 'bet_match_id': bet_match_id, 'bet_match_type_id': bet_match_type_id, 'block_index': tx['block_index'], 'settled': False, 'bull_credit': bull_credit, 'bear_credit': bear_credit, 'winner': None, 'escrow_less_fee': None, 'fee': feM sql='insert into bet_match_resolutions values(:bet_match_id, :bet_match_type_id, :block_index, :settled, :bull_credit, :bear_credit, :winner, :escrow_less_fee, :fee)' cursor.execute(sql, bindings) # Settle (if not liquidated). elif timestamp >= bet_match['deadline']: bet_match_status = 'settled' util.credit(db, bull_address, config.XCP, bull_credit, action='bet {}'.format(bet_match_status), event=tx['tx_hasM util.credit(db, bear_address, config.XCP, bear_credit, action='bet {}'.format(bet_match_status), event=tx['tx_hash']) # Pay fee to feed. util.credit(db, bet_match['feed_address'], config.XCP, fee, action='feed fee', event=tx['tx_hash']) # For logging purposes. bindings = { 'bet_match_id': bet_match_id, 'bet_match_type_id': bet_match_type_id, 'block_index': tx['block_indeM 'settled': True, 'bull_credit': bull_credit, 'bear_credit': bear_credit, 'winner': None, 'escrow_less_fee': None, 'fee': fee sql='insert into bet_match_resolutions values(:bet_match_id, :bet_match_type_id, :block_index, :settled, :bull_credit, :bear_credit, :winner, :escrow_less_fee, :fee)' cursor.execute(sql, bindings) # Equal[/NotEqM elif bet_match_type_id == equal_type_id and timestamp >= bet_match['deadline']: # Recognise tx0, tx1 as the bull, bear (in the right direction). if bet_match['tx0_bet_type'] < bet_match['tx1_bet_type']: equal_address = bet_match['tx0_address'] notequal_address = bet_match['tx1_address'] equal_address = bet_match['tx1_address'] notequal_address = bet_match['tx0_address'] # Decide M who won, and credit appropriately. if value == bet_match['target_value']: winner = 'Equal' bet_match_status = 'settled: for equal' util.credit(db, equal_address, config.XCP, escrow_less_fee, action='bet {}'.format(bet_match_status), event=tx['tx_hash']) winner = 'NotEqual' bet_match_status = 'settled: for notequal' util.credit(db, notequal_address, config.XCP, escrow_less_fee, action='bet {}M '.format(bet_match_status), event=tx['tx_hash']) # Pay fee to feed. util.credit(db, bet_match['feed_address'], config.XCP, fee, action='feed fee', event=tx['tx_hash']) # For logging purposes. bindings = { 'bet_match_id': bet_match_id, 'bet_match_type_id': bet_match_type_id, 'block_index': tx['block_index'], 'settled': None, 'bull_credit': None, 'bear_credit': None, 'winner': winner, 'escrow_less_fee': escrow_less_fee, 'fee': fee sql='insert into bet_match_resolutions values(:bet_match_id, :bet_match_type_id, :block_index, :settled, :bull_credit, :bear_credit, :winner, :escrow_less_fee, :fee)' cursor.execute(sql, bindings) # Update the bet match if bet_match_status: bindings = { 'status': bet_match_status, 'bet_match_id': utilM .make_id(bet_match['tx0_hash'], bet_match['tx1_hash']) sql='update bet_matches set status = :status where id = :bet_match_id' cursor.execute(sql, bindings) log.message(db, tx['block_index'], 'update', 'bet_matches', bindings) broadcast_bet_match_cursor.close() # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 = logging.getLogger(__name__) from counterpartylib.lib import config from counterpartylib.lib import exceptions from counterpartylib.lib import util from counterpartylib.lib import log from counterpartylib.lib import message_type cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS btcpays( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, destination TEXT, btc_amount INTEGER, order_match_id TEXT, status TEXT, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') # Disallows invalids: FOREIGN KEY (order_match_id) REFERENCES order_matches(id)) cursor.execute('''CREATE INDEX IF NOT EXISTS blockM _index_idx ON btcpays (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON btcpays (source) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS destination_idx ON btcpays (destination) ''') def validate (db, source, order_match_id, block_index): order_match = None cursor = db.cursor() cursor.execute('''SELECT * FROM order_matches \ WM HERE id = ?''', (order_match_id,)) order_matches = cursor.fetchall() if len(order_matches) == 0: problems.append('no such order match %s' % order_match_id) return None, None, None, None, order_match, problems elif len(order_matches) > 1: assert False order_match = order_matches[0] if order_match['status'] == 'expired': problems.append('order match expired') elif order_match['status'] == 'completed': blems.append('order match completed') elif order_match['status'].startswith('invalid'): problems.append('order match invalid') elif order_match['status'] != 'pending': raise exceptions.OrderError('unrecognised order match status') # Figure out to which address the BTC are being paid. # Check that source address is correct. if order_match['backward_asset'] == config.BTC: if source != order_match['tx1_address'] and not (block_index >= 313900 or config.TESTNM ET or config.REGTEST): # Protocol change. problems.append('incorrect source address') destination = order_match['tx0_address'] btc_quantity = order_match['backward_quantity'] escrowed_asset = order_match['forward_asset'] escrowed_quantity = order_match['forward_quantity'] elif order_match['forward_asset'] == config.BTC: if source != order_match['tx0_address'] and not (block_index >= 313900 or config.TESTNET or config.REGTEST): # Protocol change. problems.append('incorrect source address') destination = order_match['tx1_address'] btc_quantity = order_match['forward_quantity'] escrowed_asset = order_match['backward_asset'] escrowed_quantity = order_match['backward_quantity'] assert False return destination, btc_quantity, escrowed_asset, escrowed_quantity, order_match, problems def compose (db, source, order_match_id): tx0_hash, tx1_hash = util.parse_id(order_match_id) destination, btc_quantitM y, escrowed_asset, escrowed_quantity, order_match, problems = validate(db, source, order_match_id, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) # Warn if down to the wire. time_left = order_match['match_expire_index'] - util.CURRENT_BLOCK_INDEX if time_left < 4: logger.warning('Only {} blocks until that order match expires. The payment might not make into the blockchain in time.'.format(time_left)) if 10 - time_left < 4: logger.warning('Order maM tch has only {} confirmation(s).'.format(10 - time_left)) tx0_hash_bytes, tx1_hash_bytes = binascii.unhexlify(bytes(tx0_hash, 'utf-8')), binascii.unhexlify(bytes(tx1_hash, 'utf-8')) data = message_type.pack(ID) data += struct.pack(FORMAT, tx0_hash_bytes, tx1_hash_bytes) return (source, [(destination, btc_quantity)], data) def parse (db, tx, message): cursor = db.cursor() # Unpack message. if len(message) != LENGTH: raise exceptions.UnpackError _bytes, tx1_hash_bytes = struct.unpack(FORMAT, message) tx0_hash, tx1_hash = binascii.hexlify(tx0_hash_bytes).decode('utf-8'), binascii.hexlify(tx1_hash_bytes).decode('utf-8') order_match_id = util.make_id(tx0_hash, tx1_hash) status = 'valid' except (exceptions.UnpackError, struct.error) as e: tx0_hash, tx1_hash, order_match_id = None, None, None status = 'invalid: could not unpack' if status == 'valid': destination, btc_quantity, escrowed_asset, escrowed_quaM ntity, order_match, problems = validate(db, tx['source'], order_match_id, tx['block_index']) if problems: order_match = None status = 'invalid: ' + '; '.join(problems) if status == 'valid': # BTC must be paid all at once. if tx['btc_amount'] >= btc_quantity: # Credit source address for the currency that he bought with the bitcoins. util.credit(db, tx['source'], escrowed_asset, escrowed_quantity, action='btcpay', event=tx['tx_hash']) status = 'valid' # Update order match. bindings = { 'status': 'completed', 'order_match_id': order_match_id sql='update order_matches set status = :status where id = :order_match_id' cursor.execute(sql, bindings) log.message(db, tx['block_index'], 'update', 'order_matches', bindings) # Update give and get order status as filled if order_match is completed if util.enabled('btc_orM bindings = { 'status': 'pending', 'tx0_hash': tx0_hash, 'tx1_hash': tx1_hash sql='select * from order_matches where status = :status and ((tx0_hash in (:tx0_hash, :tx1_hash)) or ((tx1_hash in (:tx0_hash, :tx1_hash))))' cursor.execute(sql, bindings) order_matches = cursor.fetchall() if len(order_matches) == 0: # mark both btc gM et and give orders as filled when order_match is completed and give or get remaining = 0 bindings = { 'status': 'filled', 'tx0_hash': tx0_hash, 'tx1_hash': tx1_hash } sql='update orders set status = :status where ((tx_hash in (:tx0_hash, :tx1_hash)) and ((give_remaining = 0) or (get_remaining = 0)))' cursor.execute(sql, bindings) else: # always mark btc get order as filled when order_match is completed and give or get remaining = 0 bindings = { 'status': 'filled', 'source': tx['destination'], 'tx0_hash': tx0_hash, 'tx1_hash': tx1_hash } sql='update orders set status = :status where ((tx_hash in (:tx0_hash, :tx1_hash)) and ((give_remaining = 0) or (get_remaining = 0)) and (source = :sourcM cursor.execute(sql, bindings) # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'destination': tx['destination'], 'btc_amount': tx['btc_amount'], 'order_match_id': order_match_id, 'status': status, if "integer overflow" not in status: sql = 'insert into btcpays values(:M tx_index, :tx_hash, :block_index, :source, :destination, :btc_amount, :order_match_id, :status)' cursor.execute(sql, bindings) logger.warn("Not storing [btcpay] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(bindings), )) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 logger = logging.getLogger(__name__) from fractions import Fraction from counterpartylib.lib import (config, exceptions, util) """Burn {} to earn {} during a special period of time.""".format(config.BTC, config.XCP) def initialise (db): cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS burns( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, burned INTEGER, earned INTEGER, status TEXT, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS status_idx ON burns (status) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS address_idx ON burns (source) ''') def validate (db, source, destination, quantity, block_indexM # Check destination address. if destination != config.UNSPENDABLE: problems.append('wrong destination address') if not isinstance(quantity, int): problems.append('quantity must be in satoshis') return problems if quantity < 0: problems.append('negative quantity') # Try to make sure that the burned funds won't go to waste. if block_index < config.BURN_START - 1: problems.append('too early') elif block_index > config.M problems.append('too late') def compose (db, source, quantity, overburn=False): cursor = db.cursor() destination = config.UNSPENDABLE problems = validate(db, source, destination, quantity, util.CURRENT_BLOCK_INDEX, overburn=overburn) if problems: raise exceptions.ComposeError(problems) # Check that a maximum of 1 BTC total is burned per address. burns = list(cursor.execute('''SELECT * FROM burns WHERE (status = ? AND source = ?)''', ('valid', source))M already_burned = sum([burn['burned'] for burn in burns]) if quantity > (1 * config.UNIT - already_burned) and not overburn: raise exceptions.ComposeError('1 {} may be burned per address'.format(config.BTC)) return (source, [(destination, quantity)], None) def parse (db, tx, MAINNET_BURNS, message=None): burn_parse_cursor = db.cursor() if config.TESTNET or config.REGTEST: problems = [] status = 'valid' if status == 'valid': blems = validate(db, tx['source'], tx['destination'], tx['btc_amount'], tx['block_index'], overburn=False) if problems: status = 'invalid: ' + '; '.join(problems) if tx['btc_amount'] != None: sent = tx['btc_amount'] sent = 0 if status == 'valid': # Calculate quantity of XCP earned. (Maximum 1 BTC in total, ever.) cursor = db.cursor() cursor.execute('''SELECT * FROM burns WHERE (status = ? AND sourM ce = ?)''', ('valid', tx['source'])) burns = cursor.fetchall() already_burned = sum([burn['burned'] for burn in burns]) ONE = 1 * config.UNIT max_burn = ONE - already_burned if sent > max_burn: burned = max_burn # Exceeded maximum burn; earn what you can. else: burned = sent total_time = config.BURN_END - config.BURN_START partial_time = config.BURN_END - tx['block_index'] multiplier = (1000 + (500 * FractM ion(partial_time, total_time))) earned = round(burned * multiplier) # Credit source address with earned XCP. util.credit(db, tx['source'], config.XCP, earned, action='burn', event=tx['tx_hash']) burned = 0 earned = 0 tx_index = tx['tx_index'] tx_hash = tx['tx_hash'] block_index = tx['block_index'] source = tx['source'] # Mainnet burns are hard line = MAINNM ET_BURNS[tx['tx_hash']] except KeyError: util.credit(db, line['source'], config.XCP, int(line['earned']), action='burn', event=line['tx_hash']) tx_index = tx['tx_index'] tx_hash = line['tx_hash'] block_index = line['block_index'] source = line['source'] burned = line['burned'] earned = line['earned'] status = 'valid' # Add parsed transaction to message-type # TODO: store sent in table 'tx_index': tx_index, 'tx_hash': tx_hash, 'block_index': block_index, 'source': source, 'burned': burned, 'earned': earned, 'status': status, if "integer overflow" not in status: sql = 'insert into burns values(:tx_index, :tx_hash, :block_index, :source, :burned, :earned, :status)' burn_parse_cursor.execute(sql, bindings) logger.warn("Not storing [burn] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debuM g("Bindings: %s" % (json.dumps(bindings), )) burn_parse_cursor.close() # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 offer_hash is the hash of either a bet or an order. logger = logging.getLogger(__name__) from counterpartylib.lib import (config, exceptions, util, message_type) from . import (order, bet, rps) def initialise (db): cursor = db.cursorM cursor.execute('''CREATE TABLE IF NOT EXISTS cancels( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, offer_hash TEXT, status TEXT, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') # Offer hash is not a foreign key. (And itM cannot be, because of some invalid cancels.) cursor.execute('''CREATE INDEX IF NOT EXISTS cancels_block_index_idx ON cancels (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON cancels (source) ''') def validate (db, source, offer_hash): # TODO: make query only if necessary cursor = db.cursor() cursor.execute('''SELECT * from orders WHERE tx_hash = ?''', (offer_hash,))M orders = list(cursor) cursor.execute('''SELECT * from bets WHERE tx_hash = ?''', (offer_hash,)) bets = list(cursor) cursor.execute('''SELECT * from rps WHERE tx_hash = ?''', (offer_hash,)) rps = list(cursor) offer_type = None if orders: offer_type = 'order' elif bets: offer_type = 'bet' elif rps: offer_type = 'rps' else: problems = ['no open offer with that hash'] offers = orders + bets + rps if offer['source'] != source: problems.append('incorrect source address') if offer['status'] != 'open': problems.append('offer not open') return offer, offer_type, problems def compose (db, source, offer_hash): # Check that offer exists. offer, offer_type, problems = validate(db, source, offer_hash) if problems: raise exceptions.ComposeError(problems) offer_hash_bytes = binascii.unhexlify(bytes(offer_hash, 'utf-8')) data = message_type.packM data += struct.pack(FORMAT, offer_hash_bytes) return (source, [], data) def parse (db, tx, message): cursor = db.cursor() # Unpack message. if len(message) != LENGTH: raise exceptions.UnpackError offer_hash_bytes = struct.unpack(FORMAT, message)[0] offer_hash = binascii.hexlify(offer_hash_bytes).decode('utf-8') status = 'valid' except (exceptions.UnpackError, struct.error) as e: offer_hash = None status = 'invalid: couldM if status == 'valid': offer, offer_type, problems = validate(db, tx['source'], offer_hash) if problems: status = 'invalid: ' + '; '.join(problems) if status == 'valid': # Cancel if order. if offer_type == 'order': order.cancel_order(db, offer, 'cancelled', tx['block_index']) # Cancel if bet. elif offer_type == 'bet': bet.cancel_bet(db, offer, 'cancelled', tx['block_index']) # Cancel if rps. f offer_type == 'rps': rps.cancel_rps(db, offer, 'cancelled', tx['block_index']) # If neither order or bet, mark as invalid. assert False # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'offer_hash': offer_hash, 'status': status, if "integer overflow" not in status: sql='INSERT INTO cancels VALUES (:tx_index, :tx_hash, :block_index, :source, :offer_hash, :status)' cursor.execute(sql, bindings) logger.warn("Not storing [cancel] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(bindings), )) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 """Destroy a quantity of an asset.""" logger = logging.geM from counterpartylib.lib import util from counterpartylib.lib import config from counterpartylib.lib import script from counterpartylib.lib import message_type from counterpartylib.lib.script import AddressError from counterpartylib.lib.exceptions import * cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS destructions( tx_index INTEGER PRIMARY KEY, M tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, asset INTEGER, quantity INTEGER, tag TEXT, status TEXT, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS status_idx ON destructions (status) '''M cursor.execute('''CREATE INDEX IF NOT EXISTS address_idx ON destructions (source) ''') def pack(db, asset, quantity, tag): data = message_type.pack(ID) if isinstance(tag, str): tag = bytes(tag.encode('utf8'))[0:MAX_TAG_LENGTH] elif isinstance(tag, bytes): tag = tag[0:MAX_TAG_LENGTH] data += struct.pack(FORMAT, util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX), quantity) def unpack(db, message): asset_id, quantity = struct.unpack(FORMAT, message[0:16]) tag = message[16:] asset = util.get_asset_name(db, asset_id, util.CURRENT_BLOCK_INDEX) except struct.error: raise UnpackError('could not unpack') except AssetIDError: raise UnpackError('asset id invalid') return asset, quantity, tag def validate (db, source, destination, asset, quantity): util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX) raise ValidateError('asset invalid') script.validate(source) except AddressError: raise ValidateError('source address invalid') raise ValidateError('destination exists') if asset == config.BTC: raise ValidateError('cannot destroy {}'.format(config.BTC)) if type(quantity) != int: raise ValidateError('quantity not integer') if quantity > config.MAX_INT: raise ValidateError('integer overflow, quanM if quantity < 0: raise ValidateError('quantity negative') if util.get_balance(db, source, asset) < quantity: raise BalanceError('balance insufficient') def compose (db, source, asset, quantity, tag): # resolve subassets asset = util.resolve_subasset_longname(db, asset) validate(db, source, None, asset, quantity) data = pack(db, asset, quantity, tag) return (source, [], data) def parse (db, tx, message): status = 'valid' asset, quantity, tM ag = None, None, None asset, quantity, tag = unpack(db, message) validate(db, tx['source'], tx['destination'], asset, quantity) util.debit(db, tx['source'], asset, quantity, 'destroy', tx['tx_hash']) except UnpackError as e: status = 'invalid: ' + ''.join(e.args) except (ValidateError, BalanceError) as e: status = 'invalid: ' + ''.join(e.args) 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'asset': asset, 'quantity': quantity, 'tag': tag, 'status': status, if "integer overflow" not in status: sql = 'insert into destructions values(:tx_index, :tx_hash, :block_index, :source, :asset, :quantity, :tag, :status)' cursor = db.cursor() cursor.execute(sql, bindings) logger.warn("Not storing [destroy] tx [M %s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(bindings), )) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # What is a dispenser? # A dispenser is a type of order where the holder address gives out a given amount # of units of an asset for a given amount of BTC satoshis received. # It's a very simple but powerful semantic to allow swaps to operate on-chain. from math import floor logger = logging.getLogger(__name__) from counterpartylib.lib import config from counterpartylib.lib import exceptions from counterpartylib.lib import util from counterpartylib.lib import log from counterpartylib.lib import message_type from counterpartylib.lib import address STATUS_OPEN_EMPTY_ADDRESS = 1 #STATUS_OPEN_ORACLE_PRICE = 20 #STATUS_OPEN_ORACLE_PRICE_EMPTY_ADDRESS = 21 cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS dispensers( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, asset TEXT, give_quantity INTEGER, escrow_quantity INTEGER, satoshirate INTEGER, status INTEGER, give_remaining IM FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') # Disallows invalids: FOREIGN KEY (order_match_id) REFERENCES order_matches(id)) cursor.execute('''CREATE INDEX IF NOT EXISTS dispensers_source_idx ON dispensers (source) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS dispensers_asset_idx ON dispensers (asset) cursor.execute('''CREATE TABLE IF NOT EXISTS dispenses( tx_index INTEGER, dispense_index INTEGER, tx_hash TEXT, block_index INTEGER, source TEXT, destination TEXT, asset TEXT, dispense_quantity INTEGER, dispenser_tx_hash TEXT, PRIMARY KEY (tx_index, dispense_index, source, destM FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(dispenses)''')] if 'dispenser_tx_hash' not in columns: cursor.execute('ALTER TABLE dispenses ADD COLUMN dispenser_tx_hash TEXT') columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(dispensers)''')] if 'oracle_addrM ess' not in columns: cursor.execute('ALTER TABLE dispensers ADD COLUMN oracle_address TEXT') def validate (db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, open_address, block_index, oracle_address): order_match = None if asset == config.BTC: problems.append('cannot dispense %s' % config.BTC) return None, problems # resolve subassets asset = util.resolve_subasset_longname(db, asset) if status == STATUS_OPEM N or status == STATUS_OPEN_EMPTY_ADDRESS: if give_quantity <= 0: problems.append('give_quantity must be positive') if mainchainrate <= 0: problems.append('mainchainrate must be positive') if escrow_quantity < give_quantity: problems.append('escrow_quantity must be greater or equal than give_quantity') elif not(status == STATUS_CLOSED): problems.append('invalid status %i' % status) cursor = db.cursor() cursor.execute('''SELECT quantity M WHERE address = ? and asset = ?''', (source,asset,)) available = cursor.fetchall() if len(available) == 0: problems.append('address doesn\'t has the asset %s' % asset) elif len(available) >= 1 and available[0]['quantity'] < escrow_quantity: problems.append('address doesn\'t has enough balance of %s (%i < %i)' % (asset, available[0]['quantity'], escrow_quantity)) if status == STATUS_OPEN_EMPTY_ADDRESS and not(open_address): open_address = source status = STATUS_OPEN query_address = open_address if status == STATUS_OPEN_EMPTY_ADDRESS else source cursor.execute('''SELECT * FROM dispensers WHERE source = ? AND asset = ? AND status=?''', (query_address, asset, STATUS_OPEN)) open_dispensers = cursor.fetchall() if status == STATUS_OPEN or status == STATUS_OPEN_EMPTY_ADDRESS: if len(open_dispensers) > 0 and open_dispensers[0]['satoshirate'] != mainchainrate: problemM s.append('address has a dispenser already opened for asset %s with a different mainchainrate' % asset) if len(open_dispensers) > 0 and open_dispensers[0]['give_quantity'] != give_quantity: problems.append('address has a dispenser already opened for asset %s with a different give_quantity' % asset) elif status == STATUS_CLOSED: if len(open_dispensers) == 0: problems.append('address doesnt has an open dispenser for asset %s' % asset) == STATUS_OPEN_EMPTY_ADDRESS: cursor.execute('''SELECT count(*) cnt FROM balances WHERE address = ?''', (query_address,)) existing_balances = cursor.fetchall() if existing_balances[0]['cnt'] > 0: problems.append('cannot open on another address if it has any balance history') if len(problems) == 0: asset_id = util.generate_asset_id(asset, block_index) if asset_id == 0: problems.append('cannot dispense %s' % asset) # M How can we test this on a test vector? if oracle_address is not None and util.enabled('oracle_dispensers', block_index): last_price, last_fee, last_label, last_updated = util.get_oracle_last_price(db, oracle_address, block_index) if last_price is None: problems.append('The oracle address %s has not broadcasted any price yet' % oracle_address) if len(problems) > 0: return None, problems return asset_id, None pose (db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, open_address=None, oracle_address=None): assetid, problems = validate(db, source, asset, give_quantity, escrow_quantity, mainchainrate, status, open_address, util.CURRENT_BLOCK_INDEX, oracle_address) if problems: raise exceptions.ComposeError(problems) destination = [] data = message_type.pack(ID) data += struct.pack(FORMAT, assetid, give_quantity, escrow_quantity, mainchainrate, status) if status == STATUS_OPENM _EMPTY_ADDRESS and open_address: data += address.pack(open_address) if oracle_address is not None and util.enabled('oracle_dispensers'): oracle_fee = calculate_oracle_fee(db, escrow_quantity, give_quantity, mainchainrate, oracle_address, util.CURRENT_BLOCK_INDEX) if oracle_fee >= config.DEFAULT_REGULAR_DUST_SIZE: destination.append((oracle_address,oracle_fee)) data += address.pack(oracle_address) return (source, destination, data) lculate_oracle_fee(db, escrow_quantity, give_quantity, mainchainrate, oracle_address, block_index): last_price, last_fee, last_fiat_label, last_updated = util.get_oracle_last_price(db, oracle_address, block_index) last_fee_multiplier = (last_fee / config.UNIT) #Format mainchainrate to ######.## oracle_mainchainrate = util.satoshirate_to_fiat(mainchainrate) oracle_mainchainrate_btc = oracle_mainchainrate/last_price #Calculate the total amount earned for dispenser and M remaining = int(floor(escrow_quantity / give_quantity)) total_quantity_btc = oracle_mainchainrate_btc * remaining oracle_fee_btc = int(total_quantity_btc * last_fee_multiplier *config.UNIT) return oracle_fee_btc def parse (db, tx, message): cursor = db.cursor() # Unpack message. action_address = tx['source'] oracle_address = None assetid, give_quantity, escrow_quantity, mainchainrate, dispenser_status = struct.unpack(FORMAT, message[0:LENGTH]) read = LENGTH if dispenser_status == STATUS_OPEN_EMPTY_ADDRESS: action_address = address.unpack(message[LENGTH:LENGTH+21]) read = LENGTH + 21 if len(message) > read: oracle_address = address.unpack(message[read:read+21]) asset = util.generate_asset_name(assetid, util.CURRENT_BLOCK_INDEX) status = 'valid' except (exceptions.UnpackError, struct.error) as e: assetid, give_quantity, mainchainrate, asset = None, None, None, None status = 'invalid: could not unpack' if status == 'valid': if util.enabled("dispenser_parsing_validation", util.CURRENT_BLOCK_INDEX): asset_id, problems = validate(db, tx['source'], asset, give_quantity, escrow_quantity, mainchainrate, dispenser_status, action_address if dispenser_status == STATUS_OPEN_EMPTY_ADDRESS else None, tx['block_index'], oracle_address) problems = None if problems: status = 'invalid: ' + '; '.join(problM if dispenser_status == STATUS_OPEN or dispenser_status == STATUS_OPEN_EMPTY_ADDRESS: cursor.execute('SELECT * FROM dispensers WHERE source=:source AND asset=:asset AND status=:status', { 'source': action_address, 'asset': asset, 'status': STATUS_OPEN existing = cursor.fetchall() if len(existing) == 0: if (oracle_address != None) and utiM l.enabled('oracle_dispensers', tx['block_index']): oracle_fee = calculate_oracle_fee(db, escrow_quantity, give_quantity, mainchainrate, oracle_address, tx['block_index']) if oracle_fee >= config.DEFAULT_REGULAR_DUST_SIZE: if tx["destination"] != oracle_address or tx["btc_amount"] < oracle_fee: status = 'invalid: insufficient or non-existent oracle fee' if status == 'valid': # Create the new dispenser try: if dispenser_status == STATUS_OPEN_EMPTY_ADDRESS: cursor.execute('SELECT count(*) cnt FROM balances WHERE address=:address AND quantity > 0', { 'address': action_address }) counts = cursor.fetchall()[0] if counts['cnt'] == 0: util.debit(db, tx['source'], asset, escrow_quantity, action='open dispenser empty addr', event=tx['tx_hash']) util.credit(db, action_address, asset, escrow_quantity, action='open dispenser empty addr', event=tx['tx_hash']) util.debit(db, action_address, asset, escrow_quantity, action='open dispenser empty addr', event=tx['tx_hash']) elsM status = 'invalid: address not empty' else: util.debit(db, tx['source'], asset, escrow_quantity, action='open dispenser', event=tx['tx_hash']) except util.DebitError as e: status = 'invalid: insufficient funds' if status == 'valid': bindings = { 'tx_index': tx['tx_index'], M 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': action_address, 'asset': asset, 'give_quantity': give_quantity, 'escrow_quantity': escrow_quantity, 'satoshirate': mainchainrate, 'status': STATUS_OPEN, 'give_remaining': escrow_quantity, 'oraM cle_address': oracle_address } sql = 'insert into dispensers values(:tx_index, :tx_hash, :block_index, :source, :asset, :give_quantity, :escrow_quantity, :satoshirate, :status, :give_remaining, :oracle_address)' cursor.execute(sql, bindings) elif len(existing) == 1 and existing[0]['satoshirate'] == mainchainrate and existing[0]['give_quantity'] == give_quantity: if tx["source"]==action_address: if (oracle_address != None) and util.enabled('oracle_dispensers', tx['block_index']): oracle_fee = calculate_oracle_fee(db, escrow_quantity, give_quantity, mainchainrate, oracle_address, tx['block_index']) if oracle_fee >= config.DEFAULT_REGULAR_DUST_SIZE: if tx["destination"] != oracle_address or tx["btc_amount"] < oracle_fee: status = 'invalid: iM nsufficient or non-existent oracle fee' if status == 'valid': # Refill the dispenser by the given amount bindings = { 'source': tx['source'], 'asset': asset, 'prev_status': dispenser_status, 'give_remaining': existing[0]['give_remaining'] + escrow_quantity, M 'status': STATUS_OPEN, 'block_index': tx['block_index'], 'action':'refill dispenser', 'escrow_quantity':escrow_quantity } try: util.debit(db, tx['source'], asset, escrow_quantity, action='refill dispenser', event=tx['tx_hash']) sql = 'UPDATE dispensers SET give_remaining=:give_remaining \ WHERE source=:source AND asset=:asset AND status=:status' cursor.execute(sql, bindings) except (util.DebitError): status = 'insufficient funds' else: status = 'invalid: can only refill dispenser from source' else: status = 'can only have one open dispenser per asset per address' if dispenser_status == STATUS_CLOSED: cursor.execute('SELECT tx_index, give_remaining FROM dispensers WHERE source=:source AND asset=:asset AND status=:status', { 'source': tx['source'], 'asset': asset, 'status': STATUS_OPEN existing = cursor.fetchall() if len(existing) == 1: util.credit(db, tx['source'], asset, existing[0]['give_remaining'], action='close dispenser', eM bindings = { 'source': tx['source'], 'asset': asset, 'status': STATUS_CLOSED, 'block_index': tx['block_index'], 'tx_index': existing[0]['tx_index'] } sql = 'UPDATE dispensers SET give_remaining=0, status=:status WHERE source=:source AND asset=:asset' cursor.execute(sql, bindings) elsM status = 'dispenser inexistent' status = 'invalid: status must be one of OPEN or CLOSE' if status != 'valid': logger.warn("Not storing [dispenser] tx [%s]: %s" % (tx['tx_hash'], status)) def is_dispensable(db, address, amount): cursor = db.cursor() cursor.execute('SELECT * FROM dispensers WHERE source=:source AND status=:status', { 'source': address, 'status': STATUS_OPEN dispensers = cuM for next_dispenser in dispensers: if next_dispenser["oracle_address"] != None: last_price, last_fee, last_fiat_label, last_updated = util.get_oracle_last_price(db, next_dispenser['oracle_address'], util.CURRENT_BLOCK_INDEX) fiatrate = util.satoshirate_to_fiat(next_dispenser["satoshirate"]) if amount >= fiatrate/last_price: return True if amount >= next_dispenser["satoshirate"]: def dispense(db, tx): cursor = db.cursor() cursor.execute('SELECT * FROM dispensers WHERE source=:source AND status=:status ORDER BY asset', { 'source': tx['destination'], 'status': STATUS_OPEN dispensers = cursor.fetchall() dispense_index = 0 for dispenser in dispensers: satoshirate = dispenser['satoshirate'] give_quantity = dispenser['give_quantity'] if satoshirate > 0 and give_quantity > 0: if (dispenser['oracle_address'] != None) and util.enabled('oracle_dispensers', tx['block_index']): last_price, last_fee, last_fiat_label, last_updated = util.get_oracle_last_price(db, dispenser['oracle_address'], tx['block_index']) fiatrate = util.satoshirate_to_fiat(satoshirate) must_give = int(floor(((tx['btc_amount'] / config.UNIT) * last_price)/fiatrate)) must_give = int(floor(tx['btc_amount'] / satoshirate)) remaining = int(floor(dispenser['give_remaining'] / give_quantity)) actually_given = min(must_give, remaining) * give_quantity give_remaining = dispenser['give_remaining'] - actually_given assert give_remaining >= 0 # Skip dispense if quantity is 0 if util.enabled('zero_quantity_value_adjustment_1') and actually_given==0: continue util.credit(db, tx['source'], dispenser['asset'], actually_given, action='dispense', eveM dispenser['give_remaining'] = give_remaining if give_remaining < dispenser['give_quantity']: # close the dispenser dispenser['give_remaining'] = 0 if give_remaining > 0: # return the remaining to the owner util.credit(db, dispenser['source'], dispenser['asset'], give_remaining, action='dispenser close', event=tx['tx_hash']) dispenser['status'] = STATUS_CLOSED dispenser['block_index'] = tx['block_index'] dispenser['prev_status'] = STATUS_OPEN cursor.execute('UPDATE DISPENSERS SET give_remaining=:give_remaining, status=:status \ WHERE source=:source AND asset=:asset AND satoshirate=:satoshirate AND give_quantity=:give_quantity AND status=:prev_status', dispenser) bindings = { 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'dispense_index': dispense_index, 'block_index': tx['block_index'], 'source': tx['destination'], 'destination': tx['source'], 'asset': dispenser['asset'], 'dispense_quantity': actually_given, 'dispenser_tx_hash': dispenser['tx_hash'] sql = 'INSERT INTO dispenses(tx_index, dispense_index, tx_hash, block_index, source, destination, asset, dispense_quantity, dispenser_tx_hash) \ VALUES(:tx_index, :dispense_index, :tM x_hash, :block_index, :source, :destination, :asset, :dispense_quantity, :dispenser_tx_hash);' cursor.execute(sql, bindings) dispense_index += 1 """Pay out dividends.""" logger = logging.getLogger(__name__) from counterpartylib.lib import (config, exceptions, util, message_type) def initialise (db): cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS dividends( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, asset TEXT, dividend_asset TEXT, quantity_per_unit INTEGER, fee_paid INTEGER, status TEXT, FOREIM GN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON dividends (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON dividends (source) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS asset_idx ON dividends (asset) ''') lidate (db, source, quantity_per_unit, asset, dividend_asset, block_index): cursor = db.cursor() if asset == config.BTC: problems.append('cannot pay dividends to holders of {}'.format(config.BTC)) if asset == config.XCP: if (not block_index >= 317500) or block_index >= 320000 or config.TESTNET or config.REGTEST: # Protocol change. problems.append('cannot pay dividends to holders of {}'.format(config.XCP)) if quantity_per_unit <= 0: positive quantity per unit') if quantity_per_unit > config.MAX_INT: problems.append('integer overflow') # Examine asset. issuances = list(cursor.execute('''SELECT * FROM issuances WHERE (status = ? AND asset = ?) ORDER BY tx_index ASC''', ('valid', asset))) if not issuances: problems.append('no such asset, {}.'.format(asset)) return None, None, problems, 0 divisible = issuances[0]['divisible'] # Only issuer can pay dividends. lock_index >= 320000 or config.TESTNET or config.REGTEST: # Protocol change. if issuances[-1]['issuer'] != source: problems.append('only issuer can pay dividends') # Examine dividend asset. if dividend_asset in (config.BTC, config.XCP): dividend_divisible = True issuances = list(cursor.execute('''SELECT * FROM issuances WHERE (status = ? AND asset = ?)''', ('valid', dividend_asset))) if not issuances: problems.append('no such dividend asseM t, {}.'.format(dividend_asset)) return None, None, problems, 0 dividend_divisible = issuances[0]['divisible'] # Calculate dividend quantities. exclude_empty = False if util.enabled('zero_quantity_value_adjustment_1'): exclude_empty = True holders = util.holders(db, asset, exclude_empty) dividend_total = 0 for holder in holders: if block_index < 294500 and not (config.TESTNET or config.REGTEST): # Protocol change. if holder['escrow']: continue address = holder['address'] address_quantity = holder['address_quantity'] if block_index >= 296000 or config.TESTNET or config.REGTEST: # Protocol change. if address == source: continue dividend_quantity = address_quantity * quantity_per_unit if divisible: dividend_quantity /= config.UNIT if not util.enabled('nondivisible_dividend_fix') and not dividend_divisible: dividend_quantity /= config.UNIT # Pre-fix behaviourM if dividend_asset == config.BTC and dividend_quantity < config.DEFAULT_MULTISIG_DUST_SIZE: continue # A bit hackish. dividend_quantity = int(dividend_quantity) outputs.append({'address': address, 'address_quantity': address_quantity, 'dividend_quantity': dividend_quantity}) addresses.append(address) dividend_total += dividend_quantity if not dividend_total: problems.append('zero dividend') if dividend_asset != config.BTC: dividend_balances = list(cursoM r.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, dividend_asset))) if not dividend_balances or dividend_balances[0]['quantity'] < dividend_total: problems.append('insufficient funds ({})'.format(dividend_asset)) if not problems and dividend_asset != config.BTC: holder_count = len(set(addresses)) if block_index >= 330000 or config.TESTNET or config.REGTEST: # Protocol change. fee = int(0.0002 * config.UNIT * holder_couM balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, config.XCP))) if not balances or balances[0]['quantity'] < fee: problems.append('insufficient funds ({})'.format(config.XCP)) if not problems and dividend_asset == config.XCP: total_cost = dividend_total + fee if not dividend_balances or dividend_balances[0]['quantity'] < total_cost: problems.append('insufficient funds ({M })'.format(dividend_asset)) if fee > config.MAX_INT or dividend_total > config.MAX_INT: problems.append('integer overflow') return dividend_total, outputs, problems, fee def compose (db, source, quantity_per_unit, asset, dividend_asset): # resolve subassets asset = util.resolve_subasset_longname(db, asset) dividend_asset = util.resolve_subasset_longname(db, dividend_asset) dividend_total, outputs, problems, fee = validate(db, source, quantityM _per_unit, asset, dividend_asset, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) logger.info('Total quantity to be distributed in dividends: {} {}'.format(util.value_out(db, dividend_total, dividend_asset), dividend_asset)) if dividend_asset == config.BTC: return (source, [(output['address'], output['dividend_quantity']) for output in outputs], None) asset_id = util.get_asset_id(db, asset, util.CURRENT_BLOCK_INDEX) dividend_asset_id = util.get_asset_id(dM b, dividend_asset, util.CURRENT_BLOCK_INDEX) data = message_type.pack(ID) data += struct.pack(FORMAT_2, quantity_per_unit, asset_id, dividend_asset_id) return (source, [], data) def parse (db, tx, message): dividend_parse_cursor = db.cursor() # Unpack message. if (tx['block_index'] > 288150 or config.TESTNET or config.REGTEST) and len(message) == LENGTH_2: quantity_per_unit, asset_id, dividend_asset_id = struct.unpack(FORMAT_2, message) asset = util.getM _asset_name(db, asset_id, tx['block_index']) dividend_asset = util.get_asset_name(db, dividend_asset_id, tx['block_index']) status = 'valid' elif len(message) == LENGTH_1: quantity_per_unit, asset_id = struct.unpack(FORMAT_1, message) asset = util.get_asset_name(db, asset_id, tx['block_index']) dividend_asset = config.XCP status = 'valid' raise exceptions.UnpackError except (exceptions.UnpackError, exceptioM ns.AssetNameError, struct.error) as e: dividend_asset, quantity_per_unit, asset = None, None, None status = 'invalid: could not unpack' if dividend_asset == config.BTC: status = 'invalid: cannot pay {} dividends within protocol'.format(config.BTC) if status == 'valid': # For SQLite3 quantity_per_unit = min(quantity_per_unit, config.MAX_INT) dividend_total, outputs, problems, fee = validate(db, tx['source'], quantity_per_unit, asset, dividend_asset, block_inM dex=tx['block_index']) if problems: status = 'invalid: ' + '; '.join(problems) if status == 'valid': util.debit(db, tx['source'], dividend_asset, dividend_total, action='dividend', event=tx['tx_hash']) if tx['block_index'] >= 330000 or config.TESTNET or config.REGTEST: # Protocol change. util.debit(db, tx['source'], config.XCP, fee, action='dividend fee', event=tx['tx_hash']) for output in outputs: if not util.enabled('M dont_credit_zero_dividend') or output['dividend_quantity'] > 0: util.credit(db, output['address'], dividend_asset, output['dividend_quantity'], action='dividend', event=tx['tx_hash']) # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'asset': asset, 'dividend_asset': dividend_asset, 'quantity_per_unit': M 'fee_paid': fee, 'status': status, if "integer overflow" not in status: sql = 'insert into dividends values(:tx_index, :tx_hash, :block_index, :source, :asset, :dividend_asset, :quantity_per_unit, :fee_paid, :status)' dividend_parse_cursor.execute(sql, bindings) logger.warn("Not storing [dividend] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(bindings), )) dividend_parse_cursor.close() vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 Allow simultaneous lock and transfer. logger = logging.getLogger(__name__) from counterpartylib.lib import (config, util, exceptions, util, message_type) LENGTH_1 = 8 + 8 + 1 FORMAT_2 = '>QQ??If' LENGTH_2 = 8 + 8 + 1 + 1 + 4 + 4 SUBASSET_FORMAT = '>QQ?B' SUBASSET_FORMAT_LENGTH = 8 + 8 + 1 + 1 # NOTE: Pascal strings are used for storing descriptions for backwards cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS issuances( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, asset TEXT, quantity INTEGER, divisible BOOL, source TEXT, issuer TEXT, transfer BOOL, callable BOOL, call_date INTEGER, call_price REAL, description TEXT, fee_paid INTEGER, locked BOOL, status TEXT, asset_longname TEXT, reset BOOL, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) # Add asset_longname for sub-assets t do `ALTER TABLE IF COLUMN NOT EXISTS`. columns = [column['name'] for column in cursor.execute('''PRAGMA table_info(issuances)''')] if 'asset_longname' not in columns: cursor.execute('''ALTER TABLE issuances ADD COLUMN asset_longname TEXT''') if 'reset' not in columns: cursor.execute('''ALTER TABLE issuances ADD COLUMN reset BOOL''') # If sweep_hotfix activated, Create issuances copy, copy old data, drop old tabM le, rename new table, recreate indexes t do `ALTER TABLE IF COLUMN NOT EXISTS` nor can drop UNIQUE constraints if 'msg_index' not in columns: cursor.execute('''CREATE TABLE IF NOT EXISTS new_issuances( tx_index INTEGER, tx_hash TEXT, msg_index INTEGER DEFAULT 0, block_index INTEGER, asset TEXT, quantitM divisible BOOL, source TEXT, issuer TEXT, transfer BOOL, callable BOOL, call_date INTEGER, call_price REAL, description TEXT, fee_paid INTEGER, locked BOOL, status TEXT, asset_longname TEXT, reset BOOL, PRIMARY KEY (tx_index, msg_index), FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index), UNIQUE (tx_hash, msg_index)) ''') cursor.execute('''INSERT INTO new_issuances(tx_index, tx_hash, msg_index, block_index, asset, quantity, divisible, sourcM e, issuer, transfer, callable, call_date, call_price, description, fee_paid, locked, status, asset_longname, reset) SELECT tx_index, tx_hash, 0, block_index, asset, quantity, divisible, source, issuer, transfer, callable, call_date, call_price, description, fee_paid, locked, status, asset_longname, reset FROM issuances''', {}) cursor.execute('DROP TABLE issuances') cursor.execute('ALTER TABLE new_issuances RENAME TO issuances') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON issuances (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS valid_asset_idx ON issuances (asset, status) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS status_idx ON issuances (status) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON issuances (source) cursor.execute('''CREATE INDEX IF NOT EXISTS asset_longname_idx ON issuances (asset_longname) ''') def validate (db, source, destination, asset, quantity, divisible, lock, reset, callable_, call_date, call_price, description, subasset_parent, subasset_longname, block_index): if asset in (config.BTC, config.XCP): problems.append('cannot issue {} or {}'.format(config.BTC, config.XCP)) if call_date is None: cM if call_price is None: call_price = 0.0 if description is None: description = "" if divisible is None: divisible = True if lock is None: lock = False if reset is None: reset = False if isinstance(call_price, int): call_price = float(call_price) #^ helps especially with calls from JS based clients, where parseFloat(15) returns 15 (not 15.0), which json takes as an int if not isinstance(quantity, int): problems.append('quantity must be in satoshis') urn call_date, call_price, problems, fee, description, divisible, None, None if call_date and not isinstance(call_date, int): problems.append('call_date must be epoch integer') return call_date, call_price, problems, fee, description, divisible, None, None if call_price and not isinstance(call_price, float): problems.append('call_price must be a float') return call_date, call_price, problems, fee, description, divisible, None, None if quantity < 0: problems.append('negatM if call_price < 0: problems.append('negative call price') if call_date < 0: problems.append('negative call date') # Callable, or not. if not callable_: if block_index >= 312500 or config.TESTNET or config.REGTEST: # Protocol change. call_date = 0 call_price = 0.0 elif block_index >= 310000: # Protocol change. if call_date: problems.append('call date for non if call_priceM problems.append('call price for non # Valid re-issuance? cursor = db.cursor() cursor.execute('''SELECT * FROM issuances \ WHERE (status = ? AND asset = ?) ORDER BY tx_index ASC''', ('valid', asset)) issuances = cursor.fetchall() reissued_asset_longname = None reissuance = True last_issuance = issuances[-1] reissued_asset_longname = last_issuance['asset_loM issuance_locked = False if util.enabled('issuance_lock_fix'): for issuance in issuances: if issuance['locked']: issuance_locked = True break elif last_issuance['locked']: # before the issuance_lock_fix, only the last issuance was checked issuance_locked = True if last_issuance['issuer'] != source: problems.append('issued by another address') if (bool(last_issuanceM ['divisible']) != bool(divisible)) and ((not util.enabled("cip03", block_index)) or (not reset)): problems.append('cannot change divisibility') if bool(last_issuance['callable']) != bool(callable_): problems.append('cannot change callability') if last_issuance['call_date'] > call_date and (call_date != 0 or (block_index < 312500 and (not config.TESTNET or not config.REGTEST))): problems.append('cannot advance call date') if last_issuance['call_price'] > caM problems.append('cannot reduce call price') if issuance_locked and quantity: problems.append('locked asset and non if issuance_locked and reset: problems.append('cannot reset a locked asset') reissuance = False if description.lower() == 'lock': problems.append('cannot lock a non #if destination: # problems.append('cannot transfer a non problems.append('cannot reset a non existent asset') # validate parent ownership for subasset if subasset_longname is not None and not reissuance: cursor = db.cursor() cursor.execute('''SELECT * FROM issuances \ WHERE (status = ? AND asset = ?) ORDER BY tx_index ASC''', ('valid', subasset_parent)) parent_issuances = cursor.fetchall() cursor.close() if parent_issuances: last_parent_M issuance = parent_issuances[-1] if last_parent_issuance['issuer'] != source: problems.append('parent asset owned by another address') problems.append('parent asset not found') # validate subasset issuance is not a duplicate if subasset_longname is not None and not reissuance: cursor = db.cursor() cursor.execute('''SELECT * FROM assets \ WHERE (asset_longname = ?)''', (subasset_longname,)) assets = cursorM if len(assets) > 0: problems.append('subasset already exists') # validate that the actual asset is numeric if asset[0] != 'A': problems.append('a subasset must be a numeric asset') # Check for existence of fee funds. if quantity or (block_index >= 315000 or config.TESTNET or config.REGTEST): # Protocol change. if not reissuance or (block_index < 310000 and not config.TESTNET and not config.REGTEST): # Pay fee only upon first issuanceM . (Protocol change.) cursor = db.cursor() cursor.execute('''SELECT * FROM balances \ WHERE (address = ? AND asset = ?)''', (source, config.XCP)) balances = cursor.fetchall() cursor.close() if util.enabled('numeric_asset_names'): # Protocol change. if subasset_longname is not None and util.enabled('subassets'): # Protocol change. # subasset issuance is 0.25 fee = int(0.25M elif len(asset) >= 13: fee = 0 else: fee = int(0.5 * config.UNIT) elif block_index >= 291700 or config.TESTNET or config.REGTEST: # Protocol change. fee = int(0.5 * config.UNIT) elif block_index >= 286000 or config.TESTNET or config.REGTEST: # Protocol change. fee = 5 * config.UNIT elif block_index > 281236 or config.TESTNET or config.REGTEST: # ProtocoM fee = 5 if fee and (not balances or balances[0]['quantity'] < fee): problems.append('insufficient funds') if not (block_index >= 317500 or config.TESTNET or config.REGTEST): # Protocol change. if len(description) > 42: problems.append('description too long') call_date = min(call_date, config.MAX_INT) assert isinstance(quantity, int) if reset and util.enabled("cip03", block_index):#reset will overwrite the M if quantity > config.MAX_INT: problems.append('total quantity overflow') total = sum([issuance['quantity'] for issuance in issuances]) if total + quantity > config.MAX_INT: problems.append('total quantity overflow') if util.enabled("cip03", block_index) and reset and issuances: cursor = db.cursor() #Checking that all supply are held by the owner of the asset cursor.execute('''SELECT * FROM balances \ WHERE asset = ? AND quantity > 0''', (asset,)) balances = cursor.fetchall() cursor.close() if (len(balances) == 0): if util.asset_supply(db, asset) > 0: problems.append('Cannot reset an asset with no holder') elif (len(balances) > 1): problems.append('Cannot reset an asset with many holders') elif (len(balances) == 1): if (balances[0]['address'] != last_issuance["issuer"]): problems.append(M 'Cannot reset an asset held by a different address than the owner') #if destination and quantity: # problems.append('cannot issue and transfer simultaneously') if util.enabled('integer_overflow_fix', block_index=block_index) and (fee > config.MAX_INT or quantity > config.MAX_INT): problems.append('integer overflow') return call_date, call_price, problems, fee, description, divisible, lock, reset, reissuance, reissued_asset_longname def compose (db, source, transfer_M destination, asset, quantity, divisible, lock, reset, description): # Callability is deprecated, so for re issuances set relevant parameters # to old values; for first issuances, make uncallable. cursor = db.cursor() cursor.execute('''SELECT * FROM issuances \ WHERE (status = ? AND asset = ?) ORDER BY tx_index ASC''', ('valid', asset)) issuances = cursor.fetchall() last_issuance = issuances[-1] callable_ = last_issuM call_date = last_issuance['call_date'] call_price = last_issuance['call_price'] callable_ = False call_date = 0 call_price = 0.0 # check subasset subasset_parent = None subasset_longname = None if util.enabled('subassets'): # Protocol change. subasset_parent, subasset_longname = util.parse_subasset_from_asset_name(asset) if subasset_longname is not None: # try to find an existing subassM sa_cursor = db.cursor() sa_cursor.execute('''SELECT * FROM assets \ WHERE (asset_longname = ?)''', (subasset_longname,)) assets = sa_cursor.fetchall() sa_cursor.close() if len(assets) > 0: # this is a reissuance asset = assets[0]['asset_name'] # this is a new issuance # generate a random numeric asset id which will map to this subasset asset = util.generate_random_asset() call_date, call_price, problems, fee, description, divisible, lock, reset, reissuance, reissued_asset_longname = validate(db, source, transfer_destination, asset, quantity, divisible, lock, reset, callable_, call_date, call_price, description, subasset_parent, subasset_longname, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) asset_id = util.generate_asset_id(asset, util.CURRENT_BLOCK_INDEX) if subasset_longname is NoneM asset_format = util.get_value_by_block_index("issuance_asset_serialization_format") asset_format_length = util.get_value_by_block_index("issuance_asset_serialization_length") # Type 20 standard issuance FORMAT_2 >QQ??If # used for standard issuances and all reissuances data = message_type.pack(ID) if (len(description) <= 42) and not util.enabled('pascal_string_removed'): curr_format = FORMAT_2 + '{}p'.format(len(description) + 1)M curr_format = asset_format + '{}s'.format(len(description)) if (asset_format_length <= 19):# callbacks parameters were removed data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if lock else 0, 1 if reset else 0, description.encode('utf-8')) elif (asset_format_length <= 26): data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if callable_ else 0, call_date or 0, call_priceM or 0.0, description.encode('utf-8')) elif (asset_format_length <= 27):# param reset was inserted data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if reset else 0, 1 if callable_ else 0, call_date or 0, call_price or 0.0, description.encode('utf-8')) elif (asset_format_length <= 28):# param lock was inserted data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if lock else 0, 1 if reset else 0, 1 if callablM call_date or 0, call_price or 0.0, description.encode('utf-8')) subasset_format = util.get_value_by_block_index("issuance_subasset_serialization_format",util.CURRENT_BLOCK_INDEX) subasset_format_length = util.get_value_by_block_index("issuance_subasset_serialization_length",util.CURRENT_BLOCK_INDEX) # Type 21 subasset issuance SUBASSET_FORMAT >QQ?B # Used only for initial subasset issuance # compacts a subasset name to save space compacted_subasset_longname = util.compact_subasset_longname(subasset_longname) compacted_subasset_length = len(compacted_subasset_longname) data = message_type.pack(SUBASSET_ID) curr_format = subasset_format + '{}s'.format(compacted_subasset_length) + '{}s'.format(len(description)) if subasset_format_length <= 18: data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, compacted_subasset_length, compacted_subasset_longname, description.encoM elif subasset_format_length <= 19:# param reset was inserted data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if reset else 0, compacted_subasset_length, compacted_subasset_longname, description.encode('utf-8')) elif subasset_format_length <= 20:# param lock was inserted data += struct.pack(curr_format, asset_id, quantity, 1 if divisible else 0, 1 if lock else 0, 1 if reset else 0, compacted_subasset_length, compacted_subasset_longnaM me, description.encode('utf-8')) if transfer_destination: destination_outputs = [(transfer_destination, None)] destination_outputs = [] return (source, destination_outputs, data) def parse (db, tx, message, message_type_id): issuance_parse_cursor = db.cursor() asset_format = util.get_value_by_block_index("issuance_asset_serialization_format",tx['block_index']) asset_format_length = util.get_value_by_block_index("issuance_asset_serialization_length",tx['block_index']) subasset_format = util.get_value_by_block_index("issuance_subasset_serialization_format",tx['block_index']) subasset_format_length = util.get_value_by_block_index("issuance_subasset_serialization_length",tx['block_index']) # Unpack message. subasset_longname = None if message_type_id == SUBASSET_ID: if not util.enabled('subassets', block_index=tx['block_index']): logger.warn("subassets are not enabled at block %s" % tx['block_index']) raise exceptions.UnpackError # parse a subasset original issuance message lock = None reset = None if subasset_format_length <= 18: asset_id, quantity, divisible, compacted_subasset_length = struct.unpack(subasset_format, message[0:subasset_format_length]) elif subasset_format_length <= 19:# param reset was inserted asset_id, quantity, divisible, reset, compacted_subasset_length = struct.unpack(subasset_formatM , message[0:subasset_format_length]) elif subasset_format_length <= 20:# param lock was inserted asset_id, quantity, divisible, lock, reset, compacted_subasset_length = struct.unpack(subasset_format, message[0:subasset_format_length]) description_length = len(message) - subasset_format_length - compacted_subasset_length if description_length < 0: logger.warn("invalid subasset length: [issuance] tx [%s]: %s" % (tx['tx_haM sh'], compacted_subasset_length)) raise exceptions.UnpackError messages_format = '>{}s{}s'.format(compacted_subasset_length, description_length) compacted_subasset_longname, description = struct.unpack(messages_format, message[subasset_format_length:]) subasset_longname = util.expand_subasset_longname(compacted_subasset_longname) callable_, call_date, call_price = False, 0, 0.0 description = description.decode('utf-8') except UnicodeDecodeError: description = '' elif (tx['block_index'] > 283271 or config.TESTNET or config.REGTEST) and len(message) >= asset_format_length: # Protocol change. if (len(message) - asset_format_length <= 42) and not util.enabled('pascal_string_removed'): curr_format = asset_format + '{}p'.format(len(message) - asset_format_length) curr_format = asset_format + '{}s'.format(len(message) - asset_format_length)M lock = None reset = None if (asset_format_length <= 19):# callbacks parameters were removed asset_id, quantity, divisible, lock, reset, description = struct.unpack(curr_format, message) callable_, call_date, call_price = False, 0, 0.0 elif (asset_format_length <= 26):#the reset param didn't even exist asset_id, quantity, divisible, callable_, call_date, call_price, description = struct.unpack(curr_format, mM elif (asset_format_length <= 27):# param reset was inserted asset_id, quantity, divisible, reset, callable_, call_date, call_price, description = struct.unpack(curr_format, message) elif (asset_format_length <= 28):# param lock was inserted asset_id, quantity, divisible, lock, reset, callable_, call_date, call_price, description = struct.unpack(curr_format, message) call_price = round(call_price, 6) # TODO: arbitrary description = description.decode('utf-8') except UnicodeDecodeError: description = '' if len(message) != LENGTH_1: raise exceptions.UnpackError asset_id, quantity, divisible = struct.unpack(FORMAT_1, message) lock, reset, callable_, call_date, call_price, description = False, False, False, 0, 0.0, '' asset = util.generate_asset_name(asset_id, tx['block_index']) ##This is for backwards compatibility with assets names longer than 12 characters if asset.startswith('A'): namedAsset = util.get_asset_name(db, asset_id, tx['block_index']) if (namedAsset != 0): asset = namedAsset status = 'valid' except exceptions.AssetIDError: asset = None status = 'invalid: bad asset name' except exceptions.UnpackError as e: et, quantity, divisible, lock, reset, callable_, call_date, call_price, description = None, None, None, None, None, None, None, None, None status = 'invalid: could not unpack' # parse and validate the subasset from the message subasset_parent = None if status == 'valid' and subasset_longname is not None: # Protocol change. # ensure the subasset_longname is valid util.validate_subasset_longname(subasset_longname) subasset_parent, subasset_longnameM = util.parse_subasset_from_asset_name(subasset_longname) except exceptions.AssetNameError as e: asset = None status = 'invalid: bad subasset name' reissuance = None if status == 'valid': call_date, call_price, problems, fee, description, divisible, lock, reset, reissuance, reissued_asset_longname = validate(db, tx['source'], tx['destination'], asset, quantity, divisible, lock, reset, callable_, call_date, call_price, description, subasset_parent, subasseM t_longname, block_index=tx['block_index']) if problems: status = 'invalid: ' + '; '.join(problems) if not util.enabled('integer_overflow_fix', block_index=tx['block_index']) and 'total quantity overflow' in problems: quantity = 0 if (status == 'valid') and reset and util.enabled("cip03", tx['block_index']): balances_cursor = issuance_parse_cursor.execute('''SELECT * FROM balances WHERE asset = ? AND quantity > 0''', (asset,)) balances_result = balances_M if len(balances_result) <= 1: if len(balances_result) == 0: issuances_cursor = issuance_parse_cursor.execute('''SELECT * FROM issuances WHERE asset = ? ORDER BY tx_index DESC''', (asset,)) issuances_result = issuances_cursor.fetchall() owner_balance = 0 owner_address = issuances_result[0]['issuer'] owner_balance = balances_result[0]["quantity"] owner_address = balances_result[0]["address"] if owner_address == tx['source']: if owner_balance > 0: util.debit(db, tx['source'], asset, owner_balance, 'reset destroy', tx['tx_hash']) bindings = { 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'asset': asset, 'quantity': owner_balance, 'tag': "reset", 'status': "valid", 'reset': True, } sql = 'insert into destructions values(:tx_index, :tx_hash, :block_index, :source, :asset, :quantity, :tag, :status)' issuance_parse_cursor.execute(sql, bindings) bindings= { 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'asset': asset, 'quantity': quantity, 'divisible': divisible, 'source': tx['source'], 'issuer': tx['source'], 'transfer': False, 'callable': callable_, 'call_date': call_date, 'call_price': call_price, 'description': descM 'fee_paid': 0, 'locked': lock, 'status': status, 'reset': True, 'asset_longname': reissued_asset_longname, sql='insert into issuances values(:tx_index, :tx_hash, 0, :block_index, :asset, :quantity, :divisible, :source, :issuer, :transfer, :callable, :call_date, :call_price, :description, :fee_paid, :locked, :status, :asset_longname, :reset)' issuM ance_parse_cursor.execute(sql, bindings) # Credit. if quantity: util.credit(db, tx['source'], asset, quantity, action="reset issuance", event=tx['tx_hash']) if tx['destination']: issuer = tx['destination'] transfer = True #quantity = 0 issuer = tx['source'] transfer = False # Debit fee. if status == 'valid': util.debit(db, tx['sourM ce'], config.XCP, fee, action="issuance fee", event=tx['tx_hash']) if not isinstance(lock,bool): lock = False if status == 'valid': if (description and description.lower() == 'lock') or lock: lock = True cursor = db.cursor() issuances = list(cursor.execute('''SELECT * FROM issuances \ WHERE (status = ? AND asset = ?) M ORDER BY tx_index ASC''', ('valid', asset))) cursor.close() if len(issuances) > 0: description = issuances[-1]['description'] # Use last description if not reissuance: # Add to table of assets. bindings= { 'asset_id': str(asset_id), 'asset_name': str(asset), 'block_index': tx['block_index'], 'asset_longname': subasset_longname, sql='insert into assets values(:asset_id, :asset_name, :block_index, :asset_longname)' issuance_parse_cursor.execute(sql, bindings) if status == 'valid' and reissuance: # when reissuing, add the asset_longname to the issuances table for API lookups asset_longname = reissued_asset_longname asset_longname = subasset_longname # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'asset': asset, 'quantity': quantity, 'divisible': divisible, 'source': tx['source'], 'issuer': issuer, 'transfer': transfer, 'callable': callable_, 'call_date': call_date, 'call_price': call_price, 'description': description, 'fee_paid': fee, 'locked': loM 'reset': reset, 'status': status, 'asset_longname': asset_longname, if "integer overflow" not in status: sql='insert into issuances values(:tx_index, :tx_hash, 0, :block_index, :asset, :quantity, :divisible, :source, :issuer, :transfer, :callable, :call_date, :call_price, :description, :fee_paid, :locked, :status, :asset_longname, :reset)' issuance_parse_cursor.execute(sql, bindings) logger.warn("Not storing M [issuance] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(bindings), )) if status == 'valid' and quantity: util.credit(db, tx['source'], asset, quantity, action="issuance", event=tx['tx_hash']) issuance_parse_cursor.close() # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 # Filled orders may not be re opened, so only orders not involving BTC (and so # which cannot haveM expired order matches) may be filled. logger = logging.getLogger(__name__) from counterpartylib.lib import config from counterpartylib.lib import exceptions from counterpartylib.lib import util from counterpartylib.lib import backend from counterpartylib.lib import log from counterpartylib.lib import message_type LENGTH = 8 + 8 + 8 + 8 + 2 + 8 cursor = db.cursor() or.execute('''CREATE TABLE IF NOT EXISTS orders( tx_index INTEGER UNIQUE, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, give_asset TEXT, give_quantity INTEGER, give_remaining INTEGER, get_asset TEXT, get_quantity INTEGER, get_remaining INTEGER, expiration INTEGER, expire_index INTEGER, fee_required INTEGER, fee_required_remaining INTEGER, fee_provided INTEGER, fee_provided_remaining INTEGER, status TEXT, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index), PRIMARY KEY (tx_index, tx_hash)) ''') cursor.execute('''CREATE INDEX IF NOT EXISM block_index_idx ON orders (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS index_hash_idx ON orders (tx_index, tx_hash) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS expire_idx ON orders (expire_index, status) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS give_status_idx ON orders (give_asset, status) ''') .execute('''CREATE INDEX IF NOT EXISTS source_give_status_idx ON orders (source, give_asset, status) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS give_get_status_idx ON orders (get_asset, give_asset, status) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON orders (source) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS give_asset_idx ON orM ''') cursor.execute('''CREATE TABLE IF NOT EXISTS order_matches( id TEXT PRIMARY KEY, tx0_index INTEGER, tx0_hash TEXT, tx0_address TEXT, tx1_index INTEGER, tx1_hash TEXT, tx1_address TEXT, forward_asset TEXT, forward_quantity INTEGER, baM backward_quantity INTEGER, tx0_block_index INTEGER, tx1_block_index INTEGER, block_index INTEGER, tx0_expiration INTEGER, tx1_expiration INTEGER, match_expire_index INTEGER, fee_paid INTEGER, status TEXT, FOREIGN KEY (tx0_index, tx0_hash, tx0_block_index) REFERENCES transactions(tM x_index, tx_hash, block_index), FOREIGN KEY (tx1_index, tx1_hash, tx1_block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS match_expire_idx ON order_matches (status, match_expire_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS forward_status_idx ON order_matches (forward_asset, status) ''') cursor.execute(''M 'CREATE INDEX IF NOT EXISTS backward_status_idx ON order_matches (backward_asset, status) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS id_idx ON order_matches (id) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx0_address_idx ON order_matches (tx0_address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx1_address_idx ON order_matches (tx1_address) ''') # Order Expirations cursor.execute('''CREATE TABLE IF NOT EXISTS order_expirations( order_index INTEGER PRIMARY KEY, order_hash TEXT UNIQUE, source TEXT, block_index INTEGER, FOREIGN KEY (block_index) REFERENCES blocks(block_index), FOREIGN KEY (order_index, order_hash) REFERENCES orders(tx_index, tx_hash)) ''') cursor.execute('''M CREATE INDEX IF NOT EXISTS block_index_idx ON order_expirations (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON order_expirations (source) ''') # Order Match Expirations cursor.execute('''CREATE TABLE IF NOT EXISTS order_match_expirations( order_match_id TEXT PRIMARY KEY, tx0_address TEXT, tx1_address TEXT, M block_index INTEGER, FOREIGN KEY (order_match_id) REFERENCES order_matches(id), FOREIGN KEY (block_index) REFERENCES blocks(block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON order_match_expirations (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx0_address_idx ON order_match_expirations (tx0_address) ''') or.execute('''CREATE INDEX IF NOT EXISTS tx1_address_idx ON order_match_expirations (tx1_address) ''') def exact_penalty (db, address, block_index, order_match_id): # Penalize addresses that don t make BTC payments. If an address lets an # order match expire, expire sell BTC orders from that address. cursor = db.cursor() bad_orders = list(cursor.execute('''SELECT * FROM orders \ WHERE (source = ? AND gM ive_asset = ? AND status = ?)''', (address, config.BTC, 'open'))) for bad_order in bad_orders: cancel_order(db, bad_order, 'expired', block_index) if not (block_index >= 314250 or config.TESTNET or config.REGTEST): # Protocol change. # Order matches. bad_order_matches = list(cursor.execute('''SELECT * FROM order_matches \ WHERE ((tx0_address = ? AND forward_asset = ?) OR (tx1_address = ? AND baM ckward_asset = ?)) AND (status = ?)''', (address, config.BTC, address, config.BTC, 'pending'))) for bad_order_match in bad_order_matches: cancel_order_match(db, bad_order_match, 'expired', block_index) def cancel_order (db, order, status, block_index): cursor = db.cursor() # Update status of order. 'status': status, 'tx_hash': order['tx_hash'] sql='update orders set statuM s = :status where tx_hash = :tx_hash' cursor.execute(sql, bindings) log.message(db, block_index, 'update', 'orders', bindings) if order['give_asset'] != config.BTC: # Can util.credit(db, order['source'], order['give_asset'], order['give_remaining'], action='cancel order', event=order['tx_hash']) if status == 'expired': # Record offer expiration. bindings = { 'order_index': order['tx_index'], 'order_hash': order['tx_hash'], 'source': order['source'], 'block_index': block_index sql='insert into order_expirations values(:order_index, :order_hash, :source, :block_index)' cursor.execute(sql, bindings) def cancel_order_match (db, order_match, status, block_index): '''The only cancelling is an expiration. cursor = db.cursor() # Skip order matches just expired as a penalty. (Not very efficient.) if not (block_index >= 314250 or config.TESTNET or config.RM EGTEST): # Protocol change. order_matches = list(cursor.execute('''SELECT * FROM order_matches \ WHERE (id = ? AND status = ?)''', (order_match['id'], 'expired'))) if order_matches: cursor.close() # Update status of order match. 'status': status, 'order_match_id': order_match['id'] sql='update order_matches set status = :status whM ere id = :order_match_id' cursor.execute(sql, bindings) log.message(db, block_index, 'update', 'order_matches', bindings) order_match_id = util.make_id(order_match['tx0_hash'], order_match['tx1_hash']) # If tx0 is dead, credit address directly; if not, replenish give remaining, get remaining, and fee required remaining. orders = list(cursor.execute('''SELECT * FROM orders \ WHERE tx_index = ?''', (order_match['tx0_index'],)))M assert len(orders) == 1 tx0_order = orders[0] if tx0_order['status'] in ('expired', 'cancelled'): tx0_order_status = tx0_order['status'] if order_match['forward_asset'] != config.BTC: util.credit(db, order_match['tx0_address'], order_match['forward_asset'], order_match['forward_quantity'], action='order {}'.format(tx0_order_status), event=order_match['id']) tx0_give_remaining = tx0_order['give_remaining'] + orM der_match['forward_quantity'] tx0_get_remaining = tx0_order['get_remaining'] + order_match['backward_quantity'] if tx0_order['get_asset'] == config.BTC and (block_index >= 297000 or config.TESTNET or config.REGTEST): # Protocol change. tx0_fee_required_remaining = tx0_order['fee_required_remaining'] + order_match['fee_paid'] tx0_fee_required_remaining = tx0_order['fee_required_remaining'] tx0_order_status = tx0_order['status'] _order_status == 'filled' and util.enabled("reopen_order_when_btcpay_expires_fix", block_index)): #This case could happen if a BTCpay expires and before the expiration, the order was filled by a correct BTCpay tx0_order_status = 'open' # So, we have to open the order again bindings = { 'give_remaining': tx0_give_remaining, 'get_remaining': tx0_get_remaining, 'status': tx0_order_status, 'fee_required_remaining': tx0_fee_required_reM 'tx_hash': order_match['tx0_hash'] sql='update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, status = :status where tx_hash = :tx_hash' cursor.execute(sql, bindings) log.message(db, block_index, 'update', 'orders', bindings) # If tx1 is dead, credit address directly; if not, replenish give remaining, get remaining, and fee required remaining. orders = list(cursor.execute('M ''SELECT * FROM orders \ WHERE tx_index = ?''', (order_match['tx1_index'],))) assert len(orders) == 1 tx1_order = orders[0] if tx1_order['status'] in ('expired', 'cancelled'): tx1_order_status = tx1_order['status'] if order_match['backward_asset'] != config.BTC: util.credit(db, order_match['tx1_address'], order_match['backward_asset'], order_match['backward_quaM ntity'], action='order {}'.format(tx1_order_status), event=order_match['id']) tx1_give_remaining = tx1_order['give_remaining'] + order_match['backward_quantity'] tx1_get_remaining = tx1_order['get_remaining'] + order_match['forward_quantity'] if tx1_order['get_asset'] == config.BTC and (block_index >= 297000 or config.TESTNET or config.REGTEST): # Protocol change. tx1_fee_required_remaining = tx1_order['fee_required_remaining'] + order_match['fee_paid'] tx1_fee_required_remaining = tx1_order['fee_required_remaining'] tx1_order_status = tx1_order['status'] if (tx1_order_status == 'filled' and util.enabled("reopen_order_when_btcpay_expires_fix", block_index)): #This case could happen if a BTCpay expires and before the expiration, the order was filled by a correct BTCpay tx1_order_status = 'open' # So, we have to open the order again bindings = { 'give_remaining': tx1_give_remaining, et_remaining': tx1_get_remaining, 'status': tx1_order_status, 'fee_required_remaining': tx1_fee_required_remaining, 'tx_hash': order_match['tx1_hash'] sql='update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, status = :status where tx_hash = :tx_hash' cursor.execute(sql, bindings) log.message(db, block_index, 'update', 'orders', bindings) if block_index < 286500M : # Protocol change. # Sanity check: one of the two must have expired. tx0_order_time_left = tx0_order['expire_index'] - block_index tx1_order_time_left = tx1_order['expire_index'] - block_index assert tx0_order_time_left or tx1_order_time_left # Penalize tardiness. if block_index >= 313900 or config.TESTNET or config.REGTEST: # Protocol change. if tx0_order['status'] == 'expired' and order_match['forward_asset'] == config.BTC: exact_penalty(db, orderM _match['tx0_address'], block_index, order_match['id']) if tx1_order['status'] == 'expired' and order_match['backward_asset'] == config.BTC: exact_penalty(db, order_match['tx1_address'], block_index, order_match['id']) if block_index >= 310000 or config.TESTNET or config.REGTEST: # Protocol change. if not (block_index >= 315000 or config.TESTNET or config.REGTEST): # Protocol change. cursor.execute('''SELECT * FROM transactions\ M WHERE tx_hash = ?''', (tx0_order['tx_hash'],)) match(db, list(cursor)[0], block_index) cursor.execute('''SELECT * FROM transactions\ WHERE tx_hash = ?''', (tx1_order['tx_hash'],)) match(db, list(cursor)[0], block_index) if status == 'expired': # Record order match expiration. bindings = { 'order_match_id': order_match['id'], 'tx0_address': order_match['tx0_address'], 'tx1_address': orderM _match['tx1_address'], 'block_index': block_index sql='insert into order_match_expirations values(:order_match_id, :tx0_address, :tx1_address, :block_index)' cursor.execute(sql, bindings) def validate (db, source, give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required, block_index): cursor = db.cursor() if give_quantity > config.MAX_INT or get_quantity > config.MAX_INT or fee_required > conM fig.MAX_INT or block_index + expiration > config.MAX_INT: problems.append('integer overflow') if give_asset == config.BTC and get_asset == config.BTC: problems.append('cannot trade {} for itself'.format(config.BTC)) if not isinstance(give_quantity, int): problems.append('give_quantity must be in satoshis') return problems if not isinstance(get_quantity, int): problems.append('get_quantity must be in satoshis') return problems if not isinstance(fee_reM problems.append('fee_required must be in satoshis') return problems if not isinstance(expiration, int): problems.append('expiration must be expressed as an integer block delta') return problems if give_quantity <= 0: problems.append('non positive give quantity') if get_quantity <= 0: problems.append('non positive get quantity') if fee_required < 0: problems.append('negative fee_required') if expiration < 0: problems.append('negative expiration')M if expiration == 0 and not (block_index >= 317500 or config.TESTNET or config.REGTEST): # Protocol change. problems.append('zero expiration') if not give_quantity or not get_quantity: problems.append('zero give or zero get') cursor.execute('select * from issuances where (status = ? and asset = ?)', ('valid', give_asset)) if give_asset not in (config.BTC, config.XCP) and not cursor.fetchall(): problems.append('no such asset to give ({})'.format(give_asset)) ute('select * from issuances where (status = ? and asset = ?)', ('valid', get_asset)) if get_asset not in (config.BTC, config.XCP) and not cursor.fetchall(): problems.append('no such asset to get ({})'.format(get_asset)) if expiration > config.MAX_EXPIRATION: problems.append('expiration overflow') def compose (db, source, give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required): cursor = db.cursor() # resolve subassets give_asset = util.resolve_subasset_longname(db, give_asset) get_asset = util.resolve_subasset_longname(db, get_asset) # Check balance. if give_asset != config.BTC: balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, give_asset))) if (not balances or balances[0]['quantity'] < give_quantity): raise exceptions.ComposeError('insufficient funds') problems = validate(db, source, give_asset, give_quantity, get_asset, get_quM antity, expiration, fee_required, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(problems) give_id = util.get_asset_id(db, give_asset, util.CURRENT_BLOCK_INDEX) get_id = util.get_asset_id(db, get_asset, util.CURRENT_BLOCK_INDEX) data = message_type.pack(ID) data += struct.pack(FORMAT, give_id, give_quantity, get_id, get_quantity, expiration, fee_required) return (source, [], data) def parse (db, tx, message): cursor = db.cursor() # Unpack message. if len(message) != LENGTH: raise exceptions.UnpackError give_id, give_quantity, get_id, get_quantity, expiration, fee_required = struct.unpack(FORMAT, message) give_asset = util.get_asset_name(db, give_id, tx['block_index']) get_asset = util.get_asset_name(db, get_id, tx['block_index']) status = 'open' except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e: give_asset, give_quaM ntity, get_asset, get_quantity, expiration, fee_required = 0, 0, 0, 0, 0, 0 status = 'invalid: could not unpack' if status == 'open': price = util.price(get_quantity, give_quantity) except ZeroDivisionError: price = 0 order_parse_cursor.execute('''SELECT * FROM balances \ WHERE (address = ? AND asset = ?)''', (tx['source'], give_asset)) balances = list(order_parse_cursorM if give_asset != config.BTC: if not balances: give_quantity = 0 balance = balances[0]['quantity'] if balance < give_quantity: give_quantity = balance get_quantity = int(price * give_quantity) problems = validate(db, tx['source'], give_asset, give_quantity, get_asset, get_quantity, expiration, fee_required, tx['block_index']) if problems: status = 'invalid: ' + '; '.join(prM if util.enabled('btc_order_minimum'): min_btc_quantity = 0.001 * config.UNIT # 0.001 BTC if util.enabled('btc_order_minimum_adjustment_1'): min_btc_quantity = 0.00001 * config.UNIT # 0.00001 BTC if (give_asset == config.BTC and give_quantity < min_btc_quantity) or (get_asset == config.BTC and get_quantity < min_btc_quantity): if problems: status += '; btc order below minimum' else: status = 'invalid: btc order below minimum' # Debit give quantity. (Escrow.) if status == 'open': if give_asset != config.BTC: # No need (or way) to debit BTC. util.debit(db, tx['source'], give_asset, give_quantity, action='open order', event=tx['tx_hash']) # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source']M 'give_asset': give_asset, 'give_quantity': give_quantity, 'give_remaining': give_quantity, 'get_asset': get_asset, 'get_quantity': get_quantity, 'get_remaining': get_quantity, 'expiration': expiration, 'expire_index': tx['block_index'] + expiration, 'fee_required': fee_required, 'fee_required_remaining': fee_required, 'fee_provided': tx['fee'], 'fee_provided_remaining': tx['fee'], 'status': status, "integer overflow" not in status: sql = 'insert into orders values(:tx_index, :tx_hash, :block_index, :source, :give_asset, :give_quantity, :give_remaining, :get_asset, :get_quantity, :get_remaining, :expiration, :expire_index, :fee_required, :fee_required_remaining, :fee_provided, :fee_provided_remaining, :status)' order_parse_cursor.execute(sql, bindings) logger.warn("Not storing [order] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(biM if status == 'open' and tx['block_index'] != config.MEMPOOL_BLOCK_INDEX: match(db, tx) order_parse_cursor.close() def match (db, tx, block_index=None): cursor = db.cursor() # Get order in question. orders = list(cursor.execute('''SELECT * FROM orders\ WHERE (tx_index = ? AND status = ?)''', (tx['tx_index'], 'open'))) cursor.close() assert len(orders) == 1 cursor.execute('''SELECT * FROM orders \ WHERE (give_asset=? AND get_asset=? AND status=? AND tx_hash != ?)''', (tx1['get_asset'], tx1['give_asset'], 'open', tx1['tx_hash'])) tx1_give_remaining = tx1['give_remaining'] tx1_get_remaining = tx1['get_remaining'] order_matches = cursor.fetchall() if tx['block_index'] > 284500 or config.TESTNET or config.REGTEST: # Protocol change. order_matches = sorted(order_matches, key=lambda x: x['tM x_index']) # Sort by tx index second. order_matches = sorted(order_matches, key=lambda x: util.price(x['get_quantity'], x['give_quantity'])) # Sort by price first. # Get fee remaining. tx1_fee_required_remaining = tx1['fee_required_remaining'] tx1_fee_provided_remaining = tx1['fee_provided_remaining'] tx1_status = tx1['status'] for tx0 in order_matches: order_match_id = util.make_id(tx0['tx_hash'], tx1['tx_hash']) if not block_index: block_index = max(tx0['block_index'], tx1['block_index']) if tx1_status != 'open': break logger.debug('Considering: ' + tx0['tx_hash']) tx0_give_remaining = tx0['give_remaining'] tx0_get_remaining = tx0['get_remaining'] # Ignore previous matches. (Both directions, just to be sure.) cursor.execute('''SELECT * FROM order_matches WHERE id = ? ''', (util.make_id(tx0['tx_hash'], tx1['tx_hash']), )) if list(cursor): ger.debug('Skipping: previous match') continue cursor.execute('''SELECT * FROM order_matches WHERE id = ? ''', (util.make_id(tx1['tx_hash'], tx0['tx_hash']), )) if list(cursor): logger.debug('Skipping: previous match') continue # Get fee provided remaining. tx0_fee_required_remaining = tx0['fee_required_remaining'] tx0_fee_provided_remaining = tx0['fee_provided_remaining'] # Make sure that that both orderM s still have funds remaining (if order involves BTC, and so cannot be if tx0['give_asset'] == config.BTC or tx0['get_asset'] == config.BTC: # Gratuitous if tx0_give_remaining <= 0 or tx1_give_remaining <= 0: logger.debug('Skipping: negative give quantity remaining') continue if block_index >= 292000 and block_index <= 310500 and not config.TESTNET or config.REGTEST: # Protocol changes if tx0_get_remaining <= 0 or tx1_getM logger.debug('Skipping: negative get quantity remaining') continue if block_index >= 294000 or config.TESTNET or config.REGTEST: # Protocol change. if tx0['fee_required_remaining'] < 0: logger.debug('Skipping: negative tx0 fee required remaining') continue if tx0['fee_provided_remaining'] < 0: logger.debug('Skipping: negative tx0 fee provided remaining') continue if tx1_fee_provided_remaining < 0: logger.debug('Skipping: negative tx1 fee provided remaining') continue if tx1_fee_required_remaining < 0: logger.debug('Skipping: negative tx1 fee required remaining') continue # If the prices agree, make the trade. The found order sets the price, # and they trade as much as they can. tx0_price = util.price(tx0['get_qM uantity'], tx0['give_quantity']) tx1_price = util.price(tx1['get_quantity'], tx1['give_quantity']) tx1_inverse_price = util.price(tx1['give_quantity'], tx1['get_quantity']) # Protocol change. if tx['block_index'] < 286000: tx1_inverse_price = util.price(1, tx1_price) logger.debug('Tx0 Price: {}; Tx1 Inverse Price: {}'.format(float(tx0_price), float(tx1_inverse_price))) if tx0_price > tx1_inverse_price: logger.debug('Skipping: price mismatch.') logger.debug('Potential forward quantities: {}, {}'.format(tx0_give_remaining, int(util.price(tx1_give_remaining, tx0_price)))) forward_quantity = int(min(tx0_give_remaining, int(util.price(tx1_give_remaining, tx0_price)))) logger.debug('Forward Quantity: {}'.format(forward_quantity)) backward_quantity = round(forward_quantity * tx0_price) logger.debug('Backward Quantity: {}'.format(backward_quantity)) if not forward_quantity: logger.debug('Skipping: zero forward quantity.') continue if block_index >= 286500 or config.TESTNET or config.REGTEST: # Protocol change. if not backward_quantity: logger.debug('Skipping: zero backward quantity.') continue forward_asset, backward_asset = tx1['get_asset'], tx1['give_asset'] if block_index >= 313900 or config.TESTNET or config.REGTEST: # Protocol change. min_btc_quantiM ty = 0.001 * config.UNIT # 0.001 BTC if (forward_asset == config.BTC and forward_quantity <= min_btc_quantity) or (backward_asset == config.BTC and backward_quantity <= min_btc_quantity): logger.debug('Skipping: below minimum {} quantity'.format(config.BTC)) continue # Check and update fee remainings. if block_index >= 286500 or config.TESTNET or config.REGTEST: # Protocol change. Deduct fee_required from provided_M remaining, etc., if possible (else don if tx1['get_asset'] == config.BTC: if block_index >= 310500 or config.TESTNET or config.REGTEST: # Protocol change. fee = int(tx1['fee_required'] * util.price(backward_quantity, tx1['give_quantity'])) else: fee = int(tx1['fee_required_remaining'] * util.price(forward_quantity, tx1_get_remaining)) logger.debug('Tx0 fee provided remaining:M {}; required fee: {}'.format(tx0_fee_provided_remaining / config.UNIT, fee / config.UNIT)) if tx0_fee_provided_remaining < fee: logger.debug('Skipping: tx0 fee provided remaining is too low.') continue else: tx0_fee_provided_remaining -= fee if block_index >= 287800 or config.TESTNET or config.REGTEST: # Protocol change. tx1_fee_required_remaining -= fM elif tx1['give_asset'] == config.BTC: if block_index >= 310500 or config.TESTNET or config.REGTEST: # Protocol change. fee = int(tx0['fee_required'] * util.price(backward_quantity, tx0['give_quantity'])) else: fee = int(tx0['fee_required_remaining'] * util.price(backward_quantity, tx0_get_remaining)) logger.debug('Tx1 fee provided remaining: {}; required fee: {}'.format(tx1_fee_provM ided_remaining / config.UNIT, fee / config.UNIT)) if tx1_fee_provided_remaining < fee: logger.debug('Skipping: tx1 fee provided remaining is too low.') continue else: tx1_fee_provided_remaining -= fee if block_index >= 287800 or config.TESTNET or config.REGTEST: # Protocol change. tx0_fee_required_remaining -= fee else: # Don if tx1['get_asset'] == config.BTC: if tx0_fee_provided_remaining < tx1['fee_required']: continue elif tx1['give_asset'] == config.BTC: if tx1_fee_provided_remaining < tx0['fee_required']: continue if config.BTC in (tx1['give_asset'], tx1['get_asset']): status = 'pending' status = 'completed' # Credit. util.credit(db, tx1['source'], tx1['get_asset'], forward_quantity, action='order match', event=order_match_id) util.credit(db, tx0['source'], tx0['get_asset'], backward_quantity, action='order match', event=order_match_id) # Debit the order, even if it involves giving bitcoins, and so one # can't debit the sending account. # Get remainings may be negative. tx0_give_remaining -= forward_quantity tx0_get_remaining -= baM tx1_give_remaining -= backward_quantity tx1_get_remaining -= forward_quantity # Update give_remaining, get_remaining. tx0_status = 'open' if tx0_give_remaining <= 0 or (tx0_get_remaining <= 0 and (block_index >= 292000 or config.TESTNET or config.REGTEST)): # Protocol change if tx0['give_asset'] != config.BTC and tx0['get_asset'] != config.BTC: # Fill order, and recredit give_remainiM tx0_status = 'filled' util.credit(db, tx0['source'], tx0['give_asset'], tx0_give_remaining, event=tx1['tx_hash'], action='filled') bindings = { 'give_remaining': tx0_give_remaining, 'get_remaining': tx0_get_remaining, 'fee_required_remaining': tx0_fee_required_remaining, 'fee_provided_remaining': tx0_fee_provided_remaining, 'status': tx0_status, 'tx_hash': tx0['tx_hM sql='update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, fee_provided_remaining = :fee_provided_remaining, status = :status where tx_hash = :tx_hash' cursor.execute(sql, bindings) log.message(db, block_index, 'update', 'orders', bindings) if tx1_give_remaining <= 0 or (tx1_get_remaining <= 0 and (block_index >= 292000 or config.TESTNET or config.REGTM EST)): # Protocol change if tx1['give_asset'] != config.BTC and tx1['get_asset'] != config.BTC: # Fill order, and recredit give_remaining. tx1_status = 'filled' util.credit(db, tx1['source'], tx1['give_asset'], tx1_give_remaining, event=tx0['tx_hash'], action='filled') bindings = { 'give_remaining': tx1_give_remaining, 'get_remaining': tx1_get_remaining, 'fee_required_remaining'M : tx1_fee_required_remaining, 'fee_provided_remaining': tx1_fee_provided_remaining, 'status': tx1_status, 'tx_hash': tx1['tx_hash'] sql='update orders set give_remaining = :give_remaining, get_remaining = :get_remaining, fee_required_remaining = :fee_required_remaining, fee_provided_remaining = :fee_provided_remaining, status = :status where tx_hash = :tx_hash' cursor.execute(sql, bindings) log.message(db, block_index,M 'update', 'orders', bindings) # Calculate when the match will expire. if block_index >= 308000 or config.TESTNET or config.REGTEST: # Protocol change. match_expire_index = block_index + 20 elif block_index >= 286500 or config.TESTNET or config.REGTEST: # Protocol change. match_expire_index = block_index + 10 match_expire_index = min(tx0['expire_index'], tx1['expire_index']) # Record order matM bindings = { 'id': util.make_id(tx0['tx_hash'], tx['tx_hash']), 'tx0_index': tx0['tx_index'], 'tx0_hash': tx0['tx_hash'], 'tx0_address': tx0['source'], 'tx1_index': tx1['tx_index'], 'tx1_hash': tx1['tx_hash'], 'tx1_address': tx1['source'], 'forward_asset': forward_asset, 'forward_quantity': forward_quantity, 'backward_asset': backward_asset, 'backward_quantity': backward_quantity, 'tx0_block_index': tx0['block_index'], 'tx1_block_index': tx1['block_index'], 'block_index': block_index, 'tx0_expiration': tx0['expiration'], 'tx1_expiration': tx1['expiration'], 'match_expire_index': match_expire_index, 'fee_paid': fee, 'status': status, sql='insert into order_matches values(:id, :tx0_indexM , :tx0_hash, :tx0_address, :tx1_index, :tx1_hash, :tx1_address, :forward_asset, :forward_quantity, :backward_asset, :backward_quantity, :tx0_block_index, :tx1_block_index, :block_index, :tx0_expiration, :tx1_expiration, :match_expire_index, :fee_paid, :status)' cursor.execute(sql, bindings) if tx1_status == 'filled': break def expire (db, block_index): cursor = db.cursor() # Expire orders and give refunds for the quantity give_remainiM ng (if non-zero; if not BTC). cursor.execute('''SELECT * FROM orders \ WHERE (status = ? AND expire_index < ?)''', ('open', block_index)) orders = list(cursor) for order in orders: cancel_order(db, order, 'expired', block_index) # Expire order_matches for BTC with no BTC. cursor.execute('''SELECT * FROM order_matches \ WHERE (status = ? and match_expire_index < ?)''', ('pending', block_index)) order_matches = list(cursor) ch in order_matches: cancel_order_match(db, order_match, 'expired', block_index) # Expire btc sell order if match expires if util.enabled('btc_sell_expire_on_match_expire'): # Check for other pending order matches involving either tx0_hash or tx1_hash bindings = { 'status': 'pending', 'tx0_hash': order_match['tx0_hash'], 'tx1_hash': order_match['tx1_hash'] sql='select * from order_matches whereM status = :status and ((tx0_hash in (:tx0_hash, :tx1_hash)) or ((tx1_hash in (:tx0_hash, :tx1_hash))))' cursor.execute(sql, bindings) order_matches_pending = cursor.fetchall() # Set BTC sell order status as expired only if there are no pending order matches if len(order_matches_pending) == 0: if order_match['backward_asset'] == "BTC" and order_match['status'] == "expired": cursor.execute('''SELECT * FROM orders \ M WHERE tx_hash = ?''', (order_match['tx1_hash'],)) cancel_order(db, list(cursor)[0], 'expired', block_index) if order_match['forward_asset'] == "BTC" and order_match['status'] == "expired": cursor.execute('''SELECT * FROM orders \ WHERE tx_hash = ?''', (order_match['tx0_hash'],)) cancel_order(db, list(cursor)[0], 'expired', block_index) if block_index >= 315000 or config.TESTNET oM r config.REGTEST: # Protocol change. for order_match in order_matches: cursor.execute('''SELECT * FROM transactions\ WHERE tx_hash = ?''', (order_match['tx0_hash'],)) match(db, list(cursor)[0], block_index) cursor.execute('''SELECT * FROM transactions\ WHERE tx_hash = ?''', (order_match['tx1_hash'],)) match(db, list(cursor)[0], block_index) xpandtab shiftwidth=4 softtabstop=4 Transaction 1: rps (Open the game) source: address used to play the game wager: amount to bet move_random_hash: sha256(sha256(move + random)) (stored as bytes, 16 bytes random) possible_moves: arbitrary odd number >= 3 expiration: how many blocks the game is valid Matching conditions: - tx0_possible_moves = tx1_possible_moves - tx0_wager = tx1_wager Transaction 2: rpsresolve (Resolve the game) source: same address as first transaM random: 16 bytes random move: the move number rps_match_id: matching id from counterpartylib.lib import config from counterpartylib.lib import exceptions from counterpartylib.lib import util from counterpartylib.lib import log from counterpartylib.lib import message_type # possible_moves wager move_random_hash expiration LENGTH = 2 + 8 + 32 + 4 def initialise (db): cursor = db.cursM # RPS (Rock-Paper-Scissors) cursor.execute('''CREATE TABLE IF NOT EXISTS rps( tx_index INTEGER UNIQUE, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, possible_moves INTEGER, wager INTEGER, move_random_hash TEXT, expiration INTEGER, expire_index INTEGER, status TEXT, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index), PRIMARY KEY (tx_index, tx_hash)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON rps (source) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS matching_idx ON rps (wager, possible_moves) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS M status_idx ON rps (status) ''') cursor.execute('''CREATE TABLE IF NOT EXISTS rps_matches( id TEXT PRIMARY KEY, tx0_index INTEGER, tx0_hash TEXT, tx0_address TEXT, tx1_index INTEGER, tx1_hash TEXT, tx1_address TEXT, tx0_move_random_hash TEXT, tx1_move_random_hash TEXT, wager INTEGER, possible_moves INTEGER, tx0_block_index INTEGER, tx1_block_index INTEGER, block_index INTEGER, tx0_expiration INTEGER, tx1_expiration INTEGER, match_expire_index INTEGER, status TEXT, FOREIGN KEY (tx0_index, tx0_hash, tx0_block_index) REFERENCES transactions(tx_index, tx_hash, block_index), FOREIGN KEY (tx1_index, tx1_hash, tx1_block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS rps_match_expire_idx ON rps_matches (status, match_expire_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS rps_tx0_address_idx ON rps_matches (tx0_address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS rps_tx1_address_idx ON rps_matches (tx1_address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS status_idx ON rps_matches (status) ''') # RPS Expirations cursor.execute('''CREATE TABLE IF NOT EXISTS rps_expirations( rps_index INTEGER PRIMARY KEY, rps_hash TEXT UNIQUE, source TEXT, block_index INTEGER, FOREIGN KEY (block_indeM x) REFERENCES blocks(block_index), FOREIGN KEY (rps_index, rps_hash) REFERENCES rps(tx_index, tx_hash)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON rps_expirations (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON rps_expirations (source) ''') # RPS Match Expirations cursor.execute('''CREATE TABLE IF NOT EXISTS rps_match_expiM rps_match_id TEXT PRIMARY KEY, tx0_address TEXT, tx1_address TEXT, block_index INTEGER, FOREIGN KEY (rps_match_id) REFERENCES rps_matches(id), FOREIGN KEY (block_index) REFERENCES blocks(block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON rps_match_expirations (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx0_address_idx ON rps_match_expirations (tx0_address) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS tx1_address_idx ON rps_match_expirations (tx1_address) ''') def cancel_rps (db, rps, status, block_index): cursor = db.cursor() # Update status of rps. 'status': status, 'tx_hash': rps['tx_hash'] sql='''UPDATE rps SET status = :stM atus WHERE tx_hash = :tx_hash''' cursor.execute(sql, bindings) log.message(db, block_index, 'update', 'rps', bindings) util.credit(db, rps['source'], 'XCP', rps['wager'], action='recredit wager', event=rps['tx_hash']) def update_rps_match_status (db, rps_match, status, block_index): cursor = db.cursor() if status in ['expired', 'concluded: tie']: # Recredit tx0 address. util.credit(db, rps_match['tx0_address'], 'XCP', rps_match['wager'],M action='recredit wager', event=rps_match['id']) # Recredit tx1 address. util.credit(db, rps_match['tx1_address'], 'XCP', rps_match['wager'], action='recredit wager', event=rps_match['id']) elif status.startswith('concluded'): # Credit the winner winner = rps_match['tx0_address'] if status == 'concluded: first player wins' else rps_match['tx1_address'] util.credit(db, winner, 'XCP', 2 * rps_match['wager'], action='wins', event=rps_mM # Update status of rps match. 'status': status, 'rps_match_id': rps_match['id'] sql='UPDATE rps_matches SET status = :status WHERE id = :rps_match_id' cursor.execute(sql, bindings) log.message(db, block_index, 'update', 'rps_matches', bindings) def validate (db, source, possible_moves, wager, move_random_hash, expiration, block_index): if util.enabled('disable_rps'): problems.append('rps disabled'M if not isinstance(possible_moves, int): problems.append('possible_moves must be a integer') return problems if not isinstance(wager, int): problems.append('wager must be in satoshis') return problems if not isinstance(expiration, int): problems.append('expiration must be expressed as an integer block delta') return problems if not all(c in string.hexdigits for c in move_random_hash): problems.append('move_random_hash must be an hexadecimal M return problems move_random_hash_bytes = binascii.unhexlify(move_random_hash) if possible_moves < 3: problems.append('possible moves must be at least 3') if possible_moves % 2 == 0: problems.append('possible moves must be odd') problems.append('non if expiration < 0: problems.append('negative expiration') if expiration == 0 and not (block_index >= 317500 or config.TESTNET or config.REGTEST): # Protocol change. problems.append('zero expiration') if expiration > config.MAX_EXPIRATION: problems.append('expiration overflow') if len(move_random_hash_bytes) != 32: problems.append('move_random_hash must be 32 bytes in hexadecimal format') def compose(db, source, possible_moves, wager, move_random_hash, expiration): problems = validate(db, source, possible_moves, wager, move_random_hash, expiration, util.CURRENT_BLOCK_INDEX) if problems: raise exceptions.ComposeError(prM data = message_type.pack(ID) data += struct.pack(FORMAT, possible_moves, wager, binascii.unhexlify(move_random_hash), expiration) return (source, [], data) def parse(db, tx, message): rps_parse_cursor = db.cursor() # Unpack message. if len(message) != LENGTH: raise exceptions.UnpackError (possible_moves, wager, move_random_hash, expiration) = struct.unpack(FORMAT, message) status = 'open' except (exceptions.UnpackError, struct.error): (possible_moves, wager, move_random_hash, expiration) = 0, 0, '', 0 status = 'invalid: could not unpack' if status == 'open': move_random_hash = binascii.hexlify(move_random_hash).decode('utf8') rps_parse_cursor.execute('''SELECT * FROM balances \ WHERE (address = ? AND asset = ?)''', (tx['source'], 'XCP')) balances = list(rps_parse_cursor) if not balances: wager = 0 e = balances[0]['quantity'] if balance < wager: wager = balance problems = validate(db, tx['source'], possible_moves, wager, move_random_hash, expiration, tx['block_index']) if problems: status = 'invalid: {}'.format(', '.join(problems)) # Debit quantity wagered. (Escrow.) if status == 'open': util.debit(db, tx['source'], 'XCP', wager, action="open RPS", event=tx['tx_hash']) # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'possible_moves': possible_moves, 'wager': wager, 'move_random_hash': move_random_hash, 'expiration': expiration, 'expire_index': tx['block_index'] + expiration, 'status': status, sql = '''INSERT INTO rps VALUES (:tx_index, :tx_hash, :block_index, :source, :possible_moves, :wager, :move_random_hash, :expiration, :exM pire_index, :status)''' rps_parse_cursor.execute(sql, bindings) if status == 'open': match(db, tx, tx['block_index']) rps_parse_cursor.close() def match (db, tx, block_index): cursor = db.cursor() # Get rps in question. rps = list(cursor.execute('''SELECT * FROM rps WHERE tx_index = ? AND status = ?''', (tx['tx_index'], 'open'))) cursor.close() assert len(rps) == 1 possible_moves = tx1['posM wager = tx1['wager'] tx1_status = 'open' bindings = (possible_moves, 'open', wager, tx1['source']) # dont match twice same RPS already_matched = [] old_rps_matches = cursor.execute('''SELECT * FROM rps_matches WHERE tx0_hash = ? OR tx1_hash = ?''', (tx1['tx_hash'], tx1['tx_hash'])) for old_rps_match in old_rps_matches: counter_tx_hash = old_rps_match['tx1_hash'] if tx1['tx_hash'] == old_rps_match['tx0_hash'] else old_rps_match['tx0_hash'] already_matched.append(counter_tx_hash) already_matched_cond = '' if already_matched: already_matched_cond = '''AND tx_hash NOT IN ({})'''.format(','.join(['?' for e in range(0, len(already_matched))])) bindings += tuple(already_matched) sql = '''SELECT * FROM rps WHERE (possible_moves = ? AND status = ? AND wager = ? AND source != ? {}) ORDER BY tx_index LIMIT 1'''.format(already_matched_cond) rps_matches = list(cursor.execute(sql, bindings)) # update status for txn in [tx0, tx1]: bindings = { 'status': 'matched', 'tx_index': txn['tx_index'] cursor.execute('''UPDATE rps SET status = :status WHERE tx_index = :tx_index''', bindings) log.message(db, block_index, 'update', 'rps', bindings) bindings = { 'id': util.make_id(tx0['tx_hash'], tx1['tx_hash']), 'tx0_index': tx0['tx_index'], 'tx0_hash': tx0['tx_M 'tx0_address': tx0['source'], 'tx1_index': tx1['tx_index'], 'tx1_hash': tx1['tx_hash'], 'tx1_address': tx1['source'], 'tx0_move_random_hash': tx0['move_random_hash'], 'tx1_move_random_hash': tx1['move_random_hash'], 'wager': wager, 'possible_moves': possible_moves, 'tx0_block_index': tx0['block_index'], 'tx1_block_index': tx1['block_index'], 'block_index': block_index, 'tx0_expiration': tx0['expiration'], 'tx1_expiration': tx1['expiration'], 'match_expire_index': block_index + 20, 'status': 'pending' sql = '''INSERT INTO rps_matches VALUES (:id, :tx0_index, :tx0_hash, :tx0_address, :tx1_index, :tx1_hash, :tx1_address, :tx0_move_random_hash, :tx1_move_random_hash, :wager, :possible_moves, :tx0_block_index, :tx1_block_index, :blM ock_index, :tx0_expiration, :tx1_expiration, :match_expire_index, :status)''' cursor.execute(sql, bindings) def expire (db, block_index): cursor = db.cursor() # Expire rps and give refunds for the quantity wager. cursor.execute('''SELECT * FROM rps WHERE (status = ? AND expire_index < ?)''', ('open', block_index)) for rps in cursor.fetchall(): cancel_rps(db, rps, 'expired', block_index) # Record rps expirM bindings = { 'rps_index': rps['tx_index'], 'rps_hash': rps['tx_hash'], 'source': rps['source'], 'block_index': block_index sql = '''INSERT INTO rps_expirations VALUES (:rps_index, :rps_hash, :source, :block_index)''' cursor.execute(sql, bindings) # Expire rps matches expire_bindings = ('pending', 'pending and resolved', 'resolved and pending', block_index) cursor.execute('''SELECT * FROM rps_matches WHERE (status IM N (?, ?, ?) AND match_expire_index < ?)''', expire_bindings) for rps_match in cursor.fetchall(): new_rps_match_status = 'expired' # pending loses against resolved if rps_match['status'] == 'pending and resolved': new_rps_match_status = 'concluded: second player wins' elif rps_match['status'] == 'resolved and pending': new_rps_match_status = 'concluded: first player wins' update_rps_match_status(db, rps_match, new_rps_match_status, block_index) # Record rps match expiration. bindings = { 'rps_match_id': rps_match['id'], 'tx0_address': rps_match['tx0_address'], 'tx1_address': rps_match['tx1_address'], 'block_index': block_index sql = '''INSERT INTO rps_match_expirations VALUES (:rps_match_id, :tx0_address, :tx1_address, :block_index)''' cursor.execute(sql, bindings) # Rematch not expired and not resolved RPS if new_rps_match_status == 'expired': sql = '''SELECT * FROM rps WHERE tx_hash IN (?, ?) AND status = ? AND expire_index >= ?''' bindings = (rps_match['tx0_hash'], rps_match['tx1_hash'], 'matched', block_index) matched_rps = list(cursor.execute(sql, bindings)) for rps in matched_rps: cursor.execute('''UPDATE rps SET status = ? WHERE tx_index = ?''', ('open', rps['tx_index'])) # Re-debit XCP refund by close_rps_match. util.debit(db, rps['source'], 'XCP', rps['wager'M ], action='reopen RPS after matching expiration', event=rps_match['id']) # Rematch match(db, {'tx_index': rps['tx_index']}, block_index) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 logger = logging.getLogger(__name__) from counterpartylib.lib import (config, exceptions, util, message_type) # move random rps_match_id LENGTH = 2 + 16 + 32 + 32 def initialise (db): cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS rpsresolves( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, move INTEGER, random TEXT, rps_match_id TEXT, status TEXT, FOREIGN KEY (tx_indeM x, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON rpsresolves (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON rpsresolves (source) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS rps_match_id_idx ON rpsresolves (rps_match_id) ''') ef validate (db, source, move, random, rps_match_id): rps_match = None if not isinstance(move, int): problems.append('move must be a integer') return None, None, problems if not all(c in string.hexdigits for c in random): problems.append('random must be an hexadecimal string') return None, None, problems random_bytes = binascii.unhexlify(random) if len(random_bytes) != 16: problems.append('random must be 16 bytes in hexadecimal format'M return None, None, problems cursor = db.cursor() rps_matches = list(cursor.execute('''SELECT * FROM rps_matches WHERE id = ?''', (rps_match_id,))) if len(rps_matches) == 0: problems.append('no such rps match') return None, rps_match, problems elif len(rps_matches) > 1: assert False rps_match = rps_matches[0] problems.append('move must be greater than 0') elif move > rps_match['possible_moves']: problems.apM pend('move must be lower than {}'.format(rps_match['possible_moves'])) if source not in [rps_match['tx0_address'], rps_match['tx1_address']]: problems.append('invalid source address') return None, rps_match, problems if rps_match['tx0_address'] == source: rps_match_status = ['pending', 'pending and resolved'] rps_match_status = ['pending', 'resolved and pending'] move_random_hash = util.dhash(random_bytes + int(move).to_bytes(2M move_random_hash = binascii.hexlify(move_random_hash).decode('utf-8') if rps_match['tx{}_move_random_hash'.format(txn)] != move_random_hash: problems.append('invalid move or random value') return txn, rps_match, problems if rps_match['status'] == 'expired': problems.append('rps match expired') elif rps_match['status'].startswith('concluded'): problems.append('rps match concluded') elif rps_match['status'].startswith('invalid'): s.append('rps match invalid') elif rps_match['status'] not in rps_match_status: problems.append('rps already resolved') return txn, rps_match, problems def compose (db, source, move, random, rps_match_id): tx0_hash, tx1_hash = util.parse_id(rps_match_id) txn, rps_match, problems = validate(db, source, move, random, rps_match_id) if problems: raise exceptions.ComposeError(problems) # Warn if down to the wire. time_left = rps_match['match_expire_index'] - util.CURRENT_BLOCK_INDM if time_left < 4: logger.warning('Only {} blocks until that rps match expires. The conclusion might not make into the blockchain in time.'.format(time_left)) tx0_hash_bytes = binascii.unhexlify(bytes(tx0_hash, 'utf-8')) tx1_hash_bytes = binascii.unhexlify(bytes(tx1_hash, 'utf-8')) random_bytes = binascii.unhexlify(bytes(random, 'utf-8')) data = message_type.pack(ID) data += struct.pack(FORMAT, move, random_bytes, tx0_hash_bytes, tx1_hash_bytes) return (source, [], data) parse (db, tx, message): cursor = db.cursor() # Unpack message. if len(message) != LENGTH: raise exceptions.UnpackError move, random, tx0_hash_bytes, tx1_hash_bytes = struct.unpack(FORMAT, message) tx0_hash, tx1_hash = binascii.hexlify(tx0_hash_bytes).decode('utf-8'), binascii.hexlify(tx1_hash_bytes).decode('utf-8') rps_match_id = util.make_id(tx0_hash, tx1_hash) random = binascii.hexlify(random).decode('utf-8') status = 'valid' t (exceptions.UnpackError, struct.error) as e: move, random, tx0_hash, tx1_hash, rps_match_id = None, None, None, None, None status = 'invalid: could not unpack' if status == 'valid': txn, rps_match, problems = validate(db, tx['source'], move, random, rps_match_id) if problems: rps_match = None status = 'invalid: ' + '; '.join(problems) # Add parsed transaction to message-type rpsresolves_bindings = { 'tx_index': tx['tx_M 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'move': move, 'random': random, 'rps_match_id': rps_match_id, 'status': status if status == 'valid': rps_match_status = 'concluded' if rps_match['status'] == 'pending': rps_match_status = 'resolved and pending' if txn==0 else 'pending and resolved' if rps_match_status == 'concluded': counter_txn = 0 if txn ==M counter_source = rps_match['tx{}_address'.format(counter_txn)] sql = '''SELECT * FROM rpsresolves WHERE rps_match_id = ? AND source = ? AND status = ?''' counter_games = list(cursor.execute(sql, (rps_match_id, counter_source, 'valid'))) assert len(counter_games) == 1 counter_game = counter_games[0] winner = resolve_game(db, rpsresolves_bindings, counter_game) if winner == 0: rps_match_status = 'concluded:M elif winner == counter_game['tx_index']: rps_match_status = 'concluded: {} player wins'.format('first' if counter_txn == 0 else 'second') rps_match_status = 'concluded: {} player wins'.format('first' if txn == 0 else 'second') rps.update_rps_match_status(db, rps_match, rps_match_status, tx['block_index']) sql = '''INSERT INTO rpsresolves VALUES (:tx_index, :tx_hash, :block_index, :source, :move, :random, :rps_match_id, :status)''' cursor.execute(sql, rpsresolves_bindings) # https://en.wikipedia.org/wiki/Rock-paper-scissors#Additional_weapons: def resolve_game(db, resovlerps1, resovlerps2): move1 = resovlerps1['move'] move2 = resovlerps2['move'] same_parity = (move1 % 2) == (move2 % 2) if (same_parity and move1 < move2) or (not same_parity and move1 > move2): return resovlerps1['tx_index'] elif (same_parity and move1 > move2) or (not same_parity and move1 < move2): return resovlerps2M # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 from counterpartylib.lib.messages.versions import send1 from counterpartylib.lib.messages.versions import enhanced_send from counterpartylib.lib.messages.versions import mpma from counterpartylib.lib import util from counterpartylib.lib import exceptions from counterpartylib.lib import config def initialise (db): cursor = db.cursor() cursor.execute('''CM REATE TABLE IF NOT EXISTS sends( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, destination TEXT, asset TEXT, quantity INTEGER, status TEXT, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') lumn['name'] for column in cursor.execute('''PRAGMA table_info(sends)''')] # If CIP10 activated, Create Sends copy, copy old data, drop old table, rename new table, recreate indexes t do `ALTER TABLE IF COLUMN NOT EXISTS` nor can drop UNIQUE constraints if 'msg_index' not in columns: if 'memo' not in columns: cursor.execute('''CREATE TABLE IF NOT EXISTS new_sends( tx_index INTEGER, tx_hash TEXT, block_index INTEGER, source TEXT, destination TEXT, asset TEXT, quantity INTEGER, status TEXT, msg_index INTEGER DEFAULT 0, PRIMARY KEY (tx_index, msg_index), FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index), UNIQUE (tx_hash, msg_index) ON CONFLICT FAIL) ''') cursor.execute('''INSERT INTO new_sends(tx_index, tx_hash, block_index, source, destination, asset, quantity, status) SELECT tx_index, tx_hash, block_index, source, destination, asset, quantity, status FROM sends''', {}) cursor.execute('''CREATE TABLE IF NOT EXISTS new_sends( tx_index INTEGER, tx_hash TEXM block_index INTEGER, source TEXT, destination TEXT, asset TEXT, quantity INTEGER, status TEXT, memo BLOB, msg_index INTEGER DEFAULT 0, PRIMARY KEY (tx_index, msg_index), FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index), UNIQUE (tx_hash, msg_index) ON CONFLICT FAIL) cursor.execute('''INSERT INTO new_sends (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo) SELECT tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo FROM sends''', {}) cursor.execute('DROP TABLE sends') cursor.execute('ALTER TABLE new_sends RENAME TO sends') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON sends (block_index) cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON sends (source) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS destination_idx ON sends (destination) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS asset_idx ON sends (asset) ''') # Adds a memo to sends t do `ALTER TABLE IF COLUMN NOT EXISTS`. if 'memo' not in columns:M cursor.execute('''ALTER TABLE sends ADD COLUMN memo BLOB''') cursor.execute('''CREATE INDEX IF NOT EXISTS memo_idx ON sends (memo) ''') def unpack(db, message, block_index): return send1.unpack(db, message, block_index) def validate (db, source, destination, asset, quantity, block_index): return send1.validate(db, source, destination, asset, quantity, block_index) def compose (db, source, destination, asset, quantity, memo=None, memo_is_hex=False, usM e_enhanced_send=None): # special case - enhanced_send replaces send by default when it is enabled # but it can be explicitly disabled with an API parameter if util.enabled('enhanced_sends'): # Another special case, if destination, asset and quantity are arrays, it's an MPMA send if isinstance(destination, list) and isinstance(asset, list) and isinstance(quantity, list): if util.enabled('mpma_sends'): if len(destination) == len(asset) and len(asset) == len(quM # Sending memos in a MPMA message can be done by several approaches: # 1. Send a list of memos, there must be one for each send and they correspond to the sends by index # - In this case memo_is_hex should be a list with the same cardinality # 2. Send a dict with the message specific memos and the message wide memo (same for the hex specifier): # - Each dict should have 2 members: # M + list: same as case (1). An array that specifies the memo for each send # + msg_wide: the memo for the whole message. This memo will be used for sends that don't have a memo specified. Same as in (3) # 3. Send one memo (either bytes or string) and True/False in memo_is_hex. This will be interpreted as a message wide memo. if (len(destination) > config.MPMA_LIMIT): raise exceptions.ComposeError('mpma sends have a maximum of 'M +str(config.MPMA_LIMIT)+' sends') if isinstance(memo, list) and isinstance(memo_is_hex, list): # (1) implemented here if len(memo) != len(memo_is_hex): raise exceptions.ComposeError('memo and memo_is_hex lists should have the same length') elif len(memo) != len(destination): raise exceptions.ComposeError('memo/memo_is_hex lists should have the same M return mpma.compose(db, source, util.flat(zip(asset, destination, quantity, memo, memo_is_hex)), None, None) elif isinstance(memo, dict) and isinstance(memo_is_hex, dict): # (2) implemented here if not('list' in memo and 'list' in memo_is_hex and 'msg_wide' in memo and 'msg_wide' in memo_is_hex): raise exceptions.ComposeError('when specifying memo/memo_is_hex as a dict, they musM t contain keys "list" and "msg_wide"') elif len(memo['list']) != len(memo_is_hex['list']): raise exceptions.ComposeError('length of memo.list and memo_is_hex.list must be equal') elif len(memo['list']) != len(destination): raise exceptions.ComposeError('length of memo.list/memo_is_hex.list must be equal to the amount of sends') return mpma.compose(db, source, util.flat(zip(asset, destinatM ion, quantity, memo['list'], memo_is_hex['list'])), memo['msg_wide'], memo_is_hex['msg_wide']) else: # (3) the default case return mpma.compose(db, source, util.flat(zip(asset, destination, quantity)), memo, memo_is_hex) else: raise exceptions.ComposeError('destination, asset and quantity arrays must have the same amount of elements') raise exceptions.ComposeError('mpma sends areM elif use_enhanced_send is None or use_enhanced_send == True: return enhanced_send.compose(db, source, destination, asset, quantity, memo, memo_is_hex) elif memo is not None or use_enhanced_send == True: raise exceptions.ComposeError('enhanced sends are not enabled') return send1.compose(db, source, destination, asset, quantity) def parse (db, tx, message): # TODO: *args return send1.parse(db, tx, message) # vim: tabstop=8 expandtab shiftwidth=4 softtabstM from counterpartylib.lib import exceptions from counterpartylib.lib import config from counterpartylib.lib import util from counterpartylib.lib import log from counterpartylib.lib import message_type from counterpartylib.lib import address from counterpartylib.lib.exceptions import * MAX_MEMO_LENGTH = 34 # Could be higher, but we will keep it consistent with enhanced send ANTISPAM_FEE_DECIMAL = 0.5 ANTISPAM_FEE_DECIMAL * config.UNIT FLAG_BINARY_MEMO = 4 FLAGS_ALL = FLAG_BINARY_MEMO | FLAG_BALANCES | FLAG_OWNERSHIP cursor = db.cursor() cursor.execute('''CREATE TABLE IF NOT EXISTS sweeps( tx_index INTEGER PRIMARY KEY, tx_hash TEXT UNIQUE, block_index INTEGER, source TEXT, destination TEXT, flags INTEGER, status TEXT, memo BLOB, fee_paid INTEGER, FOREIGN KEY (tx_index, tx_hash, block_index) REFERENCES transactions(tx_index, tx_hash, block_index)) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS block_index_idx ON sweeps (block_index) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS source_idx ON sweeps (source) ''') cute('''CREATE INDEX IF NOT EXISTS destination_idx ON sweeps (destination) ''') cursor.execute('''CREATE INDEX IF NOT EXISTS memo_idx ON sweeps (memo) ''') def validate (db, source, destination, flags, memo, block_index): if source == destination: problems.append('destination cannot be the same as source') cursor = db.cursor() cursor.execute('''SELECT * FROM balances WHERE (address = ? ANDM asset = ?)''', (source, 'XCP')) result = cursor.fetchall() if len(result) == 0: problems.append('insufficient XCP balance for sweep. Need %s XCP for antispam fee' % ANTISPAM_FEE_DECIMAL) elif result[0]['quantity'] < ANTISPAM_FEE: problems.append('insufficient XCP balance for sweep. Need %s XCP for antispam fee' % ANTISPAM_FEE_DECIMAL) if flags > FLAGS_ALL: problems.append('invalid flags %i' % flags) elif not(flags & (FLAG_BALANCES | FLAG_OWNERSHIP))M problems.append('must specify which kind of transfer in flags') if memo and len(memo) > MAX_MEMO_LENGTH: problems.append('memo too long') def compose (db, source, destination, flags, memo): if memo is None: elif flags & FLAG_BINARY_MEMO: memo = bytes.fromhex(memo) memo = memo.encode('utf-8') memo = struct.pack(">{}s".format(len(memo)), memo) block_index = util.CURRENT_BLOCK_INDEX problems = validate(dbM , source, destination, flags, memo, block_index) if problems: raise exceptions.ComposeError(problems) short_address_bytes = address.pack(destination) data = message_type.pack(ID) data += struct.pack(FORMAT, short_address_bytes, flags) return (source, [], data) def unpack(db, message, block_index): memo_bytes_length = len(message) - LENGTH if memo_bytes_length < 0: raise exceptions.UnpackError('invalid message length') if memo_byteM s_length > MAX_MEMO_LENGTH: raise exceptions.UnpackError('memo too long') struct_format = FORMAT + ('{}s'.format(memo_bytes_length)) short_address_bytes, flags, memo_bytes = struct.unpack(struct_format, message) if len(memo_bytes) == 0: memo_bytes = None elif not(flags & FLAG_BINARY_MEMO): memo_bytes = memo_bytes.decode('utf-8') # unpack address full_address = address.unpack(short_address_bytes) except (struct.error) as e: logger.warning("sweep send unpack error: {}".format(e)) raise exceptions.UnpackError('could not unpack') 'destination': full_address, 'flags': flags, 'memo': memo_bytes, def parse (db, tx, message): cursor = db.cursor() fee_paid = round(config.UNIT/2) # Unpack message. unpacked = unpack(db, message, tx['block_index']) destination, flags, memo_bytes = unpacked['destination'], unpacked['flags'], unpackedM status = 'valid' except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e: destination, flags, memo_bytes = None, None, None status = 'invalid: could not unpack ({})'.format(e) except BalanceError: destination, flags, memo_bytes = None, None, None status = 'invalid: insufficient balance for antispam fee for sweep' except Exception as err: destination, flags, memo_bytes = None, None, None status = 'invalid: could not uM if status == 'valid': problems = validate(db, tx['source'], destination, flags, memo_bytes, tx['block_index']) if problems: status = 'invalid: ' + '; '.join(problems) if status == 'valid': util.debit(db, tx['source'], 'XCP', fee_paid, action='sweep fee', event=tx['tx_hash']) except BalanceError: destination, flags, memo_bytes = None, None, None status = 'invalid: insufficient balance for antispam fee for sweep' if status == 'valid': cursor.execute('''SELECT * FROM balances WHERE address = ?''', (tx['source'],)) balances = cursor.fetchall() if flags & FLAG_BALANCES: for balance in balances: util.debit(db, tx['source'], balance['asset'], balance['quantity'], action='sweep', event=tx['tx_hash']) util.credit(db, destination, balance['asset'], balance['quantity'], action='sweep', event=tx['tx_hash']) if flags & FLAG_OWNERSHIP: sweep_posM for balance in balances: cursor.execute('''SELECT * FROM issuances \ WHERE (status = ? AND asset = ?) ORDER BY tx_index ASC''', ('valid', balance['asset'])) issuances = cursor.fetchall() if len(issuances) > 0: last_issuance = issuances[-1] if last_issuance['issuer'] == tx['source']: bindings= { M 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'msg_index': sweep_pos, 'block_index': tx['block_index'], 'asset': balance['asset'], 'quantity': 0, 'divisible': last_issuance['divisible'], 'source': last_issuance['source'], 'issuer': destination, 'transfer': TrM 'callable': last_issuance['callable'], 'call_date': last_issuance['call_date'], 'call_price': last_issuance['call_price'], 'description': last_issuance['description'], 'fee_paid': 0, 'locked': last_issuance['locked'], 'status': status, 'asset_longname': last_issuance['asset_longname'], 'reset': False } sql='insert into issuances values(:tx_index, :tx_hash, :msg_index, :block_index, :asset, :quantity, :divisible, :source, :issuer, :transfer, :callable, :call_date, :call_price, :description, :fee_paid, :locked, :status, :asset_longname, :reset)' cursor.execute(sql, bindings) sweep_pos += 1 bindings = { 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hM 'block_index': tx['block_index'], 'source': tx['source'], 'destination': destination, 'flags': flags, 'status': status, 'memo': memo_bytes, 'fee_paid': fee_paid sql = 'insert into sweeps values(:tx_index, :tx_hash, :block_index, :source, :destination, :flags, :status, :memo, :fee_paid)' cursor.execute(sql, bindings) # Each message gets identified by itsM # The only exception is send.version1 which is 0 the first to fourth byte. # Used IDs for messages: # 1 send.enhanced_send # 21 issuance.subasset # Allocate each new type of message within the "logical" 10 number boundary # Only allocate a 10 number boundary if it makes sense #### enhanced_send.py logger = logging.getLogger(__name__) from counterpartylib.lib import (config, util, exceptions, util, message_type, address) MAX_MEMO_LENGTH = 34 def unpack(db, message, block_index): # account for memo bytes memo_bytes_length = len(message) - LENGTH if memo_bytes_length < 0: raise exceptions.UnpackError('invalid message length') if memo_bytes_length > MAX_MEMO_LENGTH: raise exceptions.UnpackError('memo too long') struct_format = FORMAT + ('{}s'.format(memo_bytes_length)) asset_id, quantity, short_address_bytes, memo_bytes = struct.unpack(struct_format, message) if len(memo_bytes) == 0: memo_bytes = None # unpack address full_address = address.unpack(short_address_bytes) # asset id to name asset = util.generate_asset_name(asset_id, block_index) if asset == config.BTC: raise exceM ptions.AssetNameError('{} not allowed'.format(config.BTC)) except (struct.error) as e: logger.warning("enhanced send unpack error: {}".format(e)) raise exceptions.UnpackError('could not unpack') except (exceptions.AssetNameError, exceptions.AssetIDError) as e: logger.warning("enhanced send invalid asset id: {}".format(e)) raise exceptions.UnpackError('asset id invalid') 'asset': asset, 'quantity': quantity, 'address': full_address, def validate (db, source, destination, asset, quantity, memo_bytes, block_index): if asset == config.BTC: problems.append('cannot send {}'.format(config.BTC)) if not isinstance(quantity, int): problems.append('quantity must be in satoshis') return problems if quantity < 0: problems.append('negative quantity') if quantity == 0: problems.append('zero quantity') if quantity > confM problems.append('integer overflow') # destination is always required if not destination: problems.append('destination is required') if memo_bytes is not None and len(memo_bytes) > MAX_MEMO_LENGTH: problems.append('memo is too long') if util.enabled('options_require_memo'): cursor = db.cursor() results = cursor.execute('SELECT options FROM addresses WHERE address=?', (destination,)) if results: result = results.fetchone() if result and util.active_options(result['options'], config.ADDRESS_OPTION_REQUIRE_MEMO): if memo_bytes is None or (len(memo_bytes) == 0): problems.append('destination requires memo') cursor.close() def compose (db, source, destination, asset, quantity, memo, memo_is_hex): cursor = db.cursor() # Just send BTC? if asset == config.BTC: return (source, M [(destination, quantity)], None) # resolve subassets asset = util.resolve_subasset_longname(db, asset) #quantity must be in int satoshi (not float, string, etc) if not isinstance(quantity, int): raise exceptions.ComposeError('quantity must be an int (in satoshi)') # Only for outgoing (incoming will overburn). balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, asset))) if not balances or balances[0]['quantity'] < quantity: raise exceptions.ComposeError('insufficient funds') # convert memo to memo_bytes based on memo_is_hex setting if memo is None: memo_bytes = b'' elif memo_is_hex: memo_bytes = bytes.fromhex(memo) memo = memo.encode('utf-8') memo_bytes = struct.pack(">{}s".format(len(memo)), memo) block_index = util.CURRENT_BLOCK_INDEX problems = validate(db, source, destination, asset, quantity, memo_bytes, block_index) if problems: raise exceptions.ComposeErM asset_id = util.get_asset_id(db, asset, block_index) short_address_bytes = address.pack(destination) data = message_type.pack(ID) data += struct.pack(FORMAT, asset_id, quantity, short_address_bytes) data += memo_bytes # return an empty array as the second argument because we don't need to send BTC dust to the recipient return (source, [], data) def parse (db, tx, message): cursor = db.cursor() # Unpack message. unpacked = unM pack(db, message, tx['block_index']) asset, quantity, destination, memo_bytes = unpacked['asset'], unpacked['quantity'], unpacked['address'], unpacked['memo'] status = 'valid' except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e: asset, quantity, destination, memo_bytes = None, None, None, None status = 'invalid: could not unpack ({})'.format(e) asset, quantity, destination, memo_bytes = None, None, None, None status = 'invalM id: could not unpack' if status == 'valid': # don't allow sends over MAX_INT at all if quantity and quantity > config.MAX_INT: status = 'invalid: quantity is too large' quantity = None if status == 'valid': problems = validate(db, tx['source'], destination, asset, quantity, memo_bytes, tx['block_index']) if problems: status = 'invalid: ' + '; '.join(problems) if status == 'valid': # verify balance is present cursor.execute('''SELM ECT * FROM balances \ WHERE (address = ? AND asset = ?)''', (tx['source'], asset)) balances = cursor.fetchall() if not balances or balances[0]['quantity'] < quantity: status = 'invalid: insufficient funds' if status == 'valid': util.debit(db, tx['source'], asset, quantity, action='send', event=tx['tx_hash']) util.credit(db, destination, asset, quantity, action='send', event=tx['tx_hash']) # log invalid transactions if quantity is None: logger.warn("Invalid send from %s with status %s. (%s)" % (tx['source'], status, tx['tx_hash'])) logger.warn("Invalid send of %s %s from %s to %s. status is %s. (%s)" % (quantity, asset, tx['source'], destination, status, tx['tx_hash'])) # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'destination': destination, 'asset': asset, 'quantity': quantity, 'status': status, 'memo': memo_bytes, if "integer overflow" not in status and "quantity must be in satoshis" not in status: sql = 'insert into sends (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo) values(:tx_index, :tx_hash, :block_index, :source, :destination, :asset, :quantity, :status, :memo)' cursor.execute(sql, bindinM logger.warn("Not storing [send] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(bindings), )) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 from bitcoin.core import key from functools import reduce from itertools import groupby logger = logging.getLogger(__name__) from bitstring import ReadError artylib.lib import (config, util, exceptions, util, message_type, address) from .mpma_util.internals import (_decode_mpmaSendDecode, _encode_mpmaSend) ID = 3 # 0x03 is this specific message type ## expected functions for message version def unpack(db, message, block_index): unpacked = _decode_mpmaSendDecode(message, block_index) except (struct.error) as e: raise exceptions.UnpackError('could not unpack') except (exceptions.AssetNameError, exceptions.AssetIDError) as e: ise exceptions.UnpackError('invalid asset in mpma send') except (ReadError) as e: raise exceptions.UnpackError('truncated data') def validate (db, source, asset_dest_quant_list, block_index): if len(asset_dest_quant_list) == 0: problems.append('send list cannot be empty') if len(asset_dest_quant_list) == 1: problems.append('send list cannot have only one element') if len(asset_dest_quant_list) > 0: # Need to manually unpack tM he tuple to avoid errors on scenarios where no memo is specified grpd = groupby([(t[0], t[1]) for t in asset_dest_quant_list]) lengrps = [len(list(grpr)) for (group, grpr) in grpd] cardinality = max(lengrps) if cardinality > 1: problems.append('cannot specify more than once a destination per asset') cursor = db.cursor() for t in asset_dest_quant_list: # Need to manually unpack the tuple to avoid errors on scenarios where no memo is specified destination = t[1] quantity = t[2] sendMemo = None if len(t) > 3: sendMemo = t[3] if asset == config.BTC: problems.append('cannot send {} to {}'.format(config.BTC, destination)) if not isinstance(quantity, int): problems.append('quantities must be an int (in satoshis) for {} to {}'.format(asset, destination)) if quantity < 0: problems.append('negative quantity for {} to {}'.format(asset, destination)) problems.append('zero quantity for {} to {}'.format(asset, destination)) # For SQLite3 if quantity > config.MAX_INT: problems.append('integer overflow for {} to {}'.format(asset, destination)) # destination is always required if not destination: problems.append('destination is required for {}'.format(asset)) if util.enabled('options_require_memo'): results = cursor.execute('SELECT options FROM addresses WHERE M address=?', (destination,)) if results: result = results.fetchone() if result and result['options'] & config.ADDRESS_OPTION_REQUIRE_MEMO and (sendMemo is None): problems.append('destination {} requires memo'.format(destination)) def compose (db, source, asset_dest_quant_list, memo, memo_is_hex): cursor = db.cursor() out_balances = util.accumulate([(t[0], t[2]) for t in asset_dest_quant_list]) asset, quantity) in out_balances: if util.enabled('mpma_subasset_support'): # resolve subassets asset = util.resolve_subasset_longname(db, asset) if not isinstance(quantity, int): raise exceptions.ComposeError('quantities must be an int (in satoshis) for {}'.format(asset)) balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, asset))) if not balances or balances[0]['quantity'] < quantity: raise exceptions.ComposeError('insufficient funds for {}'.format(asset)) block_index = util.CURRENT_BLOCK_INDEX problems = validate(db, source, asset_dest_quant_list, block_index) if problems: raise exceptions.ComposeError(problems) data = message_type.pack(ID) data += _encode_mpmaSend(db, asset_dest_quant_list, block_index, memo=memo, memo_is_hex=memo_is_hex) return (source, [], data) def parse (db, tx, message): unpacked = unpack(db, message, tM status = 'valid' except (struct.error) as e: status = 'invalid: truncated message' except (exceptions.AssetNameError, exceptions.AssetIDError) as e: status = 'invalid: invalid asset name/id' except (Exception) as e: status = 'invalid: couldn\'t unpack; %s' % e cursor = db.cursor() plain_sends = [] all_credits = [] if status == 'valid': for asset_id in unpacked: asset = util.geM t_asset_name(db, asset_id, tx['block_index']) except (exceptions.AssetNameError) as e: status = 'invalid: asset %s invalid at block index %i' % (asset_id, tx['block_index']) break cursor.execute('''SELECT * FROM balances \ WHERE (address = ? AND asset = ?)''', (tx['source'], asset_id)) balances = cursor.fetchall() if not balances: status = 'invalid: insufficient funds for asset %s, addressM %s has no balance' % (asset_id, tx['source']) break credits = unpacked[asset_id] total_sent = reduce(lambda p, t: p + t[1], credits, 0) if balances[0]['quantity'] < total_sent: status = 'invalid: insufficient funds for asset %s, needs %i' % (asset_id, total_sent) break if status == 'valid': plain_sends += map(lambda t: util.py34TupleAppend(asset_id, t), credits) all_credits += map(lamM bda t: {"asset": asset_id, "destination": t[0], "quantity": t[1]}, credits) all_debits.append({"asset": asset_id, "quantity": total_sent}) if status == 'valid': problems = validate(db, tx['source'], plain_sends, tx['block_index']) if problems: status = 'invalid:' + '; '.join(problems) if status == 'valid': for op in all_credits: util.credit(db, op['destination'], op['asset'], op['quantity'], action='mpma send', event=tx['tx_hash']) for op in alM util.debit(db, tx['source'], op['asset'], op['quantity'], action='mpma send', event=tx['tx_hash']) # Enumeration of the plain sends needs to be deterministic, so we sort them by asset and then by address plain_sends = sorted(plain_sends, key=lambda x: ''.join([x[0], x[1]])) for i, op in enumerate(plain_sends): if len(op) > 3: memo_bytes = op[3] memo_bytes = None bindings = { 'tx_M index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'asset': op[0], 'destination': op[1], 'quantity': op[2], 'status': status, 'memo': memo_bytes, 'msg_index': i sql = 'insert into sends (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo, msg_index) values(:txM _index, :tx_hash, :block_index, :source, :destination, :asset, :quantity, :status, :memo, :msg_index)' cursor.execute(sql, bindings) if status != 'valid': logger.warn("Not storing [mpma] tx [%s]: %s" % (tx['tx_hash'], status)) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 """Create and parse 'send'-type messages.""" logger = logging.getLogger(__name__) from ... import (confiM g, exceptions, util, message_type) def unpack(db, message, block_index): # Only used for `unpack` API call at the moment. asset_id, quantity = struct.unpack(FORMAT, message) asset = util.get_asset_name(db, asset_id, block_index) except struct.error: raise exceptions.UnpackError('could not unpack') except AssetNameError: raise exceptions.UnpackError('asset id invalid') 'asset': asset, 'quantity': quantity def validate (db, source, destination, asset, quantity, block_index): if asset == config.BTC: problems.append('cannot send bitcoins') # Only for parsing. if not isinstance(quantity, int): problems.append('quantity must be in satoshis') return problems if quantity < 0: problems.append('negative quantity') if quantity > config.MAX_INT: problems.append('integer M if util.enabled('send_destination_required'): # Protocol change. if not destination: problems.append('destination is required') if util.enabled('options_require_memo'): # Check destination address options cursor = db.cursor() results = cursor.execute('SELECT options FROM addresses WHERE address=?', (destination,)) result = results.fetchone() if result and util.active_options(result['options'], config.ADDREM SS_OPTION_REQUIRE_MEMO): problems.append('destination requires memo') cursor.close() def compose (db, source, destination, asset, quantity): cursor = db.cursor() # Just send BTC? if asset == config.BTC: return (source, [(destination, quantity)], None) # resolve subassets asset = util.resolve_subasset_longname(db, asset) #quantity must be in int satoshi (not float, string, etc) if not isinstance(quantity, int): raise exceptiM ons.ComposeError('quantity must be an int (in satoshi)') # Only for outgoing (incoming will overburn). balances = list(cursor.execute('''SELECT * FROM balances WHERE (address = ? AND asset = ?)''', (source, asset))) if not balances or balances[0]['quantity'] < quantity: raise exceptions.ComposeError('insufficient funds') block_index = util.CURRENT_BLOCK_INDEX problems = validate(db, source, destination, asset, quantity, block_index) if problems: raise exceptions.ComposeError(problM asset_id = util.get_asset_id(db, asset, block_index) data = message_type.pack(ID) data += struct.pack(FORMAT, asset_id, quantity) return (source, [(destination, None)], data) def parse (db, tx, message): cursor = db.cursor() # Unpack message. if len(message) != LENGTH: raise exceptions.UnpackError asset_id, quantity = struct.unpack(FORMAT, message) asset = util.get_asset_name(db, asset_id, tx['block_index']) except (exceptions.UnpackError, exceptions.AssetNameError, struct.error) as e: asset, quantity = None, None status = 'invalid: could not unpack' if status == 'valid': cursor.execute('''SELECT * FROM balances \ WHERE (address = ? AND asset = ?)''', (tx['source'], asset)) balances = cursor.fetchall() if not balances: status = 'invalid: insufficient funds' elif balances[0]['quantitM quantity = min(balances[0]['quantity'], quantity) quantity = min(quantity, config.MAX_INT) if status == 'valid': problems = validate(db, tx['source'], tx['destination'], asset, quantity, tx['block_index']) if problems: status = 'invalid: ' + '; '.join(problems) if status == 'valid': util.debit(db, tx['source'], asset, quantity, action='send', event=tx['tx_hash']) util.credit(db, tx['destination'], assetM , quantity, action='send', event=tx['tx_hash']) # Add parsed transaction to message-type 'tx_index': tx['tx_index'], 'tx_hash': tx['tx_hash'], 'block_index': tx['block_index'], 'source': tx['source'], 'destination': tx['destination'], 'asset': asset, 'quantity': quantity, 'status': status, if "integer overflow" not in status and "quantity must be in satoshis" not in status: sql = 'insert into seM nds (tx_index, tx_hash, block_index, source, destination, asset, quantity, status, memo) values(:tx_index, :tx_hash, :block_index, :source, :destination, :asset, :quantity, :status, NULL)' cursor.execute(sql, bindings) logger.warn("Not storing [send] tx [%s]: %s" % (tx['tx_hash'], status)) logger.debug("Bindings: %s" % (json.dumps(bindings), )) # vim: tabstop=8 expandtab shiftwidth=4 softtabstop=4 from bitcoin.core import key from functools import reduce from itertools import groupby from bitstring import BitArray, BitStream, ConstBitStream, ReadError logger = logging.getLogger(__name__) from counterpartylib.lib import (config, util, exceptions, util, message_type, address) ## encoding functions def _encode_constructBaseLUT(snds): # t is a tuple of the form (asset, addr, amnt [, memo, is_hex]) return sorted(list(set([t[1] for t in snM ds]))) # Sorted to make list determinist def _encode_constructBaseAssets(sends): # t is a tuple of the form (asset, addr, amnt [, memo, is_hex]) return sorted(list(set([t[0] for t in sends]))) # Sorted to make list determinist def _encode_constructLUT(sends): baseLUT = _encode_constructBaseLUT(sends) # What's this? It calculates the minimal number of bits needed to represent an item index inside the baseLUT lutNbits = math.ceil(math.log2(len(baseLUT))) "nbits": lutNbits,M "addrs": baseLUT def _encode_compressLUT(lut): return b''.join([struct.pack('>H', len(lut['addrs']))] + address.pack(addr) for addr in lut['addrs'] def _encode_memo(memo=None, is_hex=False): '''Tightly pack a memo as a Bit array''' if memo is not None: # signal a 1 bit for existence of the memo barr = BitArray('0b1') # signal a 1 bit for hex encoded memos barr.append('0b1') if type(memo) is str: # append the string as hex-string barr.append('uint:6=%i' % (len(memo) >> 1)) memo = '0x%s' % memo barr.append('uint:6=%i' % len(memo)) barr.append(memo) # signal a 0 bit for a string encoded memo barr.append('0b0') barr.append('uint:6=%i' % len(memo)) barr.append(BitArray(memo.encode('utf-8'))) # if the memo is M None, return just a 0 bit return BitArray('0b0') def _safe_tuple_index(t, i): '''Get an element from a tuple, returning None if it's out of bounds''' def _encode_constructSendList(send_asset, lut, sends): # t is a tuple of the form (asset, addr, amnt, memo, is_hex) # if there's no memo specified, memo and is_hex are None (lut['addrs'].index(t[1]), t[2], _safe_tuple_index(t, 3), _safe_tuple_index(t, 4)) for t in sends if t[0] == send_asset def _solve_asset(db, assetName, block_index): asset = util.resolve_subasset_longname(db, assetName) return util.get_asset_id(db, asset, block_index) def _encode_compressSendList(db, nbits, send, block_index): r.append('uintbe:64=%i' % _solve_asset(db, send['assetName'], block_index)) r.append('uint:%i=%i' % (nbits, len(send['sendList'])-1)) for sendItem in send['sendList']: idx = sendItem[0] r.append('uint:%i=%i' % (nbits, idx)) r.append('uintbe:64=%i' % amnt) memoStr = _encode_memo(memo=sendItem[2], is_hex=sendItem[3]) memoStr = BitArray('0b0') r.append(memoStr) def _encode_constructSends(sends): lut = _encode_constructLUT(sends) assets = _encode_constructBaseAssets(sends) "assetName": asset, "sendList": _encode_constructSendList(asset, lutM for asset in assets "sendLists": sendLists def _encode_compressSends(db, mpmaSend, block_index, memo=None, memo_is_hex=False): compressedLUT = _encode_compressLUT(mpmaSend['lut']) memo_arr = _encode_memo(memo, memo_is_hex).bin isends = '0b' + memo_arr + ''.join([ ''.join(['1', _encode_compressSendList(db, mpmaSend['lut']['nbits'], sendList, block_index).bin]) for sendList in mpmaSend['sendLists'] bstr = ''.join([isends, '0']) pad = '0' * ((8 - (len(bstr) - 2)) % 8) # That -2 is because the prefix 0b is there barr = BitArray(bstr + pad) return b''.join([ compressedLUT, def _encode_mpmaSend(db, sends, block_index, memo=None, memo_is_hex=False): mpma = _encode_constructSends(sends) send = _encode_compressSends(db, mpma, block_index, memo=memo, memo_is_hex=memo_is_hex) ## decoding functions def _decode_decodeLUT(data): ,) = struct.unpack('>H', data[0:2]) if numAddresses == 0: raise exceptions.DecodeError('address list can\'t be empty') addressList = [] bytesPerAddress = 21 for i in range(0, numAddresses): addr_raw = data[p:p+bytesPerAddress] addressList.append(address.unpack(addr_raw)) p += bytesPerAddress lutNbits = math.ceil(math.log2(numAddresses)) return addressList, lutNbits, data[p:] def _decode_decodeSendList(stream, nbits, lut, block_index): id = stream.read('uintbe:64') numRecipients = stream.read('uint:%i' % nbits) rangeLimit = numRecipients + 1 numRecipients = 1 rangeLimit = numRecipients asset = util.generate_asset_name(asset_id, block_index) for i in range(0, rangeLimit): if nbits > 0: idx = stream.read('uint:%i' % nbits) addr = lut[idx] amount = stream.read('uintbe:64') memo, is_hex M = _decode_memo(stream) if memo is not None: sendList.append((addr, amount, memo, is_hex)) sendList.append((addr, amount)) return asset, sendList def _decode_decodeSends(stream, nbits, lut, block_index): #stream = ConstBitStream(data) while stream.read('bool'): asset, sendList = _decode_decodeSendList(stream, nbits, lut, block_index) sends[asset] = sendList def _decode_memo(stream): if stream.read('booM is_hex = stream.read('bool') mlen = stream.read('uint:6') data = stream.read('bytes:%i' % mlen) if not(is_hex): # is an utf8 string data = data.decode('utf-8') return data, is_hex return None, None def _decode_mpmaSendDecode(data, block_index): lut, nbits, remain = _decode_decodeLUT(data) stream = ConstBitStream(remain) memo, is_hex = _decode_memo(stream) sends = _decode_decodeSends(stream, nbits, lut, block_inM, if memo is not None: for asset in sends: sendList = sends[asset] for idx, send in enumerate(sendList): if len(send) == 2: sendList[idx] = (send[0], send[1], memo, is_hex) AUUUUUUUUUUUHUUUUUUUUUUUUUUUUUUi !22222222222222222222222222222222222222222222222222 TUUUUUUUUUUUUUUUUUUUUUUUUUUUU FPUUUUUUUUUUUUUUUUUUUUUUUUUUUUU text/plain;charset=utf-8 SjLPQuesto dovrebbe essere il limite per gli 80 caratteri di un testo op_return ciao FjDOUT:2C73338A350D8C3395F29B96DB61A2295FC00D2FA324697D20F40623CBF42E54 3e48013a564f5d725e88da2709abb6c7G0D 4ae201a374e28e0b41697e7a5cc8a9baH0E ()*89:HIJWXYZghijwxyz ()*789:FGHIJUVWXYZdefghijstuvwxyz c/Foundry USA Pool #dropgold/ EjCs:ETH.ETH:0x18A99CF66D9d80837Ee21734fA5C3d4D4686bb1A:261054731:ss:0 DjB=:ETH.ETH:0x6b749E0134288737dfc4827ba6D90A54ab7346F6:12723469:te:0 IjGREFUND:AB61703B275F8235DF30734970CB8FDA87E5CF161D6CF5E7F56A4BC5159ADFE6 Copyright Apple Inc., 2023 %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz &'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz )4)))))4>444444>>>>>>>>KKKKKKWWWWWbbbbbbbbbb +fE9Effffffffffffffffffffffffffffffffffffffffffffffffff http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 9.0-c000 79.171c27fab, 2022/08/16-22:35:41 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmpMM:OriginalDocumentID="xmpM .did:8187f80b-58da-b441-beb6-f4facd64d755" xmpMM:DocumentID="xmp.did:49D755B5A32D11ED9141BCEBB78737A9" xmpMM:InstanceID="xmp.iid:49D755B4A32D11ED9141BCEBB78737A9" xmp:CreatorTool="Adobe Photoshop 24.1 (Windows)"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:cee66003-89ea-6e42-8659-2c4688cd6775" stRef:documentID="xmp.did:8187f80b-58da-b441-beb6-f4facd64d755"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?> &&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& This is better than a JPG MjK=:BNB.BUSD-BD1:bnb1aaahj4ye2rgavhqfm9cwgzem2tcs9xpt2f2n94:466681064925:t:30] Bj@=:ETH.ETH:0x6eb4978c5d07379Bc7759F21f30B2b53EDBA0491:455695:te:0 Ahttp://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:ICCProfile="M sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T17:30:32-05:00" xmp:MetadataDate="2023-02-02T17:30:32-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T17:30:32-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> +?7BA>7<;ENcTEI^K;<VvW^gjopoCSz 3kG<Gkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk Ahttp://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:M ICCProfile="sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T17:30:06-05:00" xmp:MetadataDate="2023-02-02T17:30:06-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T17:30:06-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> +?7BA>7<;ENcTEI^K;<VvW^gjopoCM 3kG<Gkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk c/SBICrypto.com Pool/ MjK=:BNB.BUSD-BD1:bnb1aaahj4ye2rgavhqfm9cwgzem2tcs9xpt2f2n94:467933815166:t:30 SjLPs:ETH.USDC-3606EB48:0x18A99CF66D9d80837Ee21734fA5C3d4D4686bb1A:214007541697:ss:0 9j7+:BTC/BTC::bc1q3f787hr38pmal87yxtpq8tng09q60ljjqqd759:0V Aj?=:BNB.BNB:bnb1em5ake82afp2u6xtrnl9x879689gra3ezku0cz:17364:te:0 Aj?=:ETH.ETH:0x6eb4978c5d07379Bc7759F21f30B2b53EDBA0491:39697:te:0 CjA=:BNB.BNB:bnb15cy0flkvuhuvsjpsp0tphsauprxguuzj8ekuqv:2782480:te:0 EjC=:BNB.BTCB-1DE:bnb1l65zmzsteq2swtncp74f3etcmmsjfsqu7hdrkh:7482:te:0 IjGREFUND:CEEED9FCC85F8260DF2604F5C4DD4F7F7582602EEA9E2880D11B1E5E0E4A5278 FjDOUT:7FED7BBEDB95F8D99A6E191E785B3DCAAF3054152FDC6944CF952C2BD6090EE7 FjDOUT:21FECF0DFDDCE4C43C593F46B9C53203B3B81A5A0200F648C2A60E1FD2D9F237 FjDOUT:9D1038CC280A43152E0414C3BC25CD7B000DFA4CA3C336A8A1E27DCA8C2F6EC5 IjGREFUND:04BB70BADD38ACFB6DB78A85928267CB76D9F38DE42C25E1F10F9EC37C6C38A7 Bj@=:ETH.ETH:0x2E3E96d59d3052A1B73B579f777c5241441A9E93:194320:te:0 text/plain;charset=utf-8 INDUSTRIAL SOCIETY AND ITS FUTURE 1. The Industrial Revolution and its consequences have been a disaster for the human race. They have greatly increased the life-expectancy of those of us countries, but they have destabilized society, have made life unfulfilling, have subjected human beings to indignities, have led to widespread psychological suffering (in the Third World to physical suffering as well) and have inflicted severe damage on the natural worlM continued development of technology will worsen the situation. It will certainly subject human beings to greater indignities and inflict greater damage on the natural world, it will probably lead to greater social disruption and psychological suffering, and it may lead to increased physical suffering even in 2. The industrial-technological system may survive or it may break down. If it survives, it MAY eventually achieve a low level of physical and psychologM suffering, but only after passing through a long and very painful period of adjustment and only at the cost of permanently reducing human beings and many other living organisms to engineered products and mere cogs in the social machine. Furthermore, if the system survives, the consequences will be inevitable: There is no way of reforming or modifying the system so as to prevent it from depriving people of dignity and autonomy. 3. If the system breaks down the consequences will still beM the bigger the system grows the more disastrous the results of its breakdown will be, so if it is to break down it had best break down sooner rather than 4. We therefore advocate a revolution against the industrial system. This revolution may or may not make use of violence; it may be sudden or it may be a relatively gradual process spanning a few decades. We can that. But we do outline in a very general way the measures that those who he industrial system should take in order to prepare the way for a revolution against that form of society. This is not to be a POLITICAL revolution. Its object will be to overthrow not governments but the economic and technological basis of the present society. 5. In this article we give attention to only some of the negative developments that have grown out of the industrial-technological system. Other such developments we mention only briefly or ignore altogether. This does not mean we regard these other developments as unimportant. For practical reasons we have to confine our discussion to areas that have received insufficient public attention or in which we have something new to say. For example, since there are well-developed environmental and wilderness movements, we have written very little about environmental degradation or the destruction of wild nature, even though we consider these to be highly important. THE PSYCHOLOGY OF MODERN LEFTISM 6. Almost everyone will M agree that we live in a deeply troubled society. One of the most widespread manifestations of the craziness of our world is leftism, so a discussion of the psychology of leftism can serve as an introduction to the discussion of the problems of modern society in general. 7. But what is leftism? During the first half of the 20th century leftism could have been practically identified with socialism. Today the movement is fragmented and it is not clear who can properly be called a leftist. When we M speak of leftists in this article we have in mind mainly socialists, types, feminists, gay and disability activists, animal rights activists and the like. But not everyone who is associated with one of these movements is a leftist. What we are trying to get at in discussing leftism is not so much movement or an ideology as a psychological type, or rather a collection of related types. Thus, what we will emerge more clearly inM the course of our discussion of leftist psychology. (Also, see paragraphs 227-230.) 8. Even so, our conception of leftism will remain a good deal less clear than we would wish, but there doesn t seem to be any remedy for this. All we are trying to do here is indicate in a rough and approximate way the two psychological tendencies that we believe are the main driving force of modern leftism. We by no means claim to be telling the WHOLE truth about leftist psychology. Also, our discussion M is meant to apply to modern leftism only. We leave open the question of the extent to which our discussion could be applied to the leftists of the 19th and early 20th centuries. 9. The two psychological tendencies that underlie modern leftism we call feelings of inferiority Feelings of inferiority are characteristic of modern leftism as a whole, while oversocialization is characteristic only of a certain segment of modern leftism; but this segment FEELINGS OF INFERIORITY feelings of inferiority we mean not only inferiority feelings in the strict sense but a whole spectrum of related traits; low self-esteem, feelings of powerlessness, depressive tendencies, defeatism, guilt, self- hatred, etc. We argue that modern leftists tend to have some such feelings (possibly more or less repressed) and that these feelings are decisive in determining the direction of modern leftism. 11. When someone interprets as deM rogatory almost anything that is said about him (or about groups with whom he identifies) we conclude that he has inferiority feelings or low self-esteem. This tendency is pronounced among minority rights activists, whether or not they belong to the minority groups whose rights they defend. They are hypersensitive about the words used to designate minorities and about anything that is said concerning minorities. Asian, a disabled person or a woman originally had no derogatory were merely the feminine equivalents of The negative connotations have been attached to these terms by the activists themselves. Some animal rights activists have gone so far as to reject the word and insist on its replacement by Leftish anthropologists go to great lengths to avoid anything about primitive peoples that could conceivably be interpreted as negative. They want to replace the world They seem almost paranoid about anything that might suggest that any primitive culture is inferior to our own. (We do not mean to imply that primitive cultures ARE inferior to ours. We merely point out the hypersensitivity of leftish anthropologists.) 12. Those who are most sensitive about politically incorrect not the average black ghetto- dweller, Asian immigrant, abused woman or disabled person, but a minority of activists, many of whom do not even group but come from privileged strata of society. Political correctness has its stronghold among university professors, who have secure employment with comfortable salaries, and the majority of whom are heterosexual white males from middle- to upper-middle-class families. 13. Many leftists have an intense identificationM with the problems of groups that have an image of being weak (women), defeated (American Indians), repellent (homosexuals) or otherwise inferior. The leftists themselves feel that these groups are inferior. They would never admit to themselves that they have such feelings, but it is precisely because they do see these groups as inferior that they identify with their problems. (We do not mean to suggest that women, Indians, etc. ARE inferior; we are only making a point about leftiM 14. Feminists are desperately anxious to prove that women are as strong and as capable as men. Clearly they are nagged by a fear that women may NOT be as strong and as capable as men. 15. Leftists tend to hate anything that has an image of being strong, good and successful. They hate America, they hate Western civilization, they hate white males, they hate rationality. The reasons that leftists give for hating the West, etc. clearly do not correspond with their real motiveM They SAY they hate the West because it is warlike, imperialistic, sexist, ethnocentric and so forth, but where these same faults appear in socialist countries or in primitive cultures, the leftist finds excuses for them, or at best he GRUDGINGLY admits that they exist; whereas he ENTHUSIASTICALLY points out (and often greatly exaggerates) these faults where they appear in Western civilization. Thus it is clear that these faults are not the s real motive for hating AmeM rica and the West. He hates America and the West because they are strong and successful. etc., play little role in the liberal and leftist vocabulary. The leftist is anti-individualistic, pro-collectivist. He wants society to s problems for them, satisfy everyone s needs for them, take care of them. He is not the sort of person who has an inner sense of fidence in his ability to solve his own problems and satisfy his own needs. The leftist is antagonistic to the concept of competition because, deep inside, he feels like a loser. 17. Art forms that appeal to modern leftish intellectuals tend to focus on sordidness, defeat and despair, or else they take an orgiastic tone, throwing off rational control as if there were no hope of accomplishing anything through rational calculation and all that was left was to immerse oneself in the sensM ations of the moment. 18. Modern leftish philosophers tend to dismiss reason, science, objective reality and to insist that everything is culturally relative. It is true that one can ask serious questions about the foundations of scientific knowledge and about how, if at all, the concept of objective reality can be defined. But it is obvious that modern leftish philosophers are not simply cool-headed logicians systematically analyzing the foundations of knowledge. They are deeply invoM lved emotionally in their attack on truth and reality. They attack these concepts because of their own psychological needs. For one thing, their attack is an outlet for hostility, and, to the extent that it is successful, it satisfies the drive for power. More importantly, the leftist hates science and rationality because they classify certain beliefs as true (i.e., successful, superior) and other beliefs as false (i.e., failed, inferior). The leftist s feelings of inferiority run soM cannot tolerate any classification of some things as successful or superior and other things as failed or inferior. This also underlies the rejection by many leftists of the concept of mental illness and of the utility of IQ tests. Leftists are antagonistic to genetic explanations of human abilities or behavior because such explanations tend to make some persons appear superior or inferior to others. Leftists prefer to give society the credit or blame for an individuM s ability or lack of it. Thus if a person is it is not his fault, but society s, because he has not been brought up properly. 19. The leftist is not typically the kind of person whose feelings of inferiority make him a braggart, an egotist, a bully, a self-promoter, a ruthless competitor. This kind of person has not wholly lost faith in himself. He has a deficit in his sense of power and self-worth, but he can still conceive of himself as having the capacity to bM efforts to make himself strong produce his unpleasant behavior. [1] But the leftist is too far gone for that. His feelings of inferiority are so ingrained that he cannot conceive of himself as individually strong and valuable. Hence the collectivism of the leftist. He can feel strong only as a member of a large organization or a mass movement with which he identifies 20. Notice the masochistic tendency of leftist tactics. Leftists protest by in front of vehicles, they intentionally provoke police or racists to abuse them, etc. These tactics may often be effective, but many leftists use them not as a means to an end but because they PREFER masochistic tactics. Self-hatred is a leftist trait. 21. Leftists may claim that their activism is motivated by compassion or by moral principles, and moral principle does play a role for the leftist of the oversocialized type. But compassion and moral principle cannot be the s for leftist activism. Hostility is too prominent a component of leftist behavior; so is the drive for power. Moreover, much leftist behavior is not rationally calculated to be of benefit to the people whom the leftists claim to be trying to help. For example, if one believes that affirmative action is good for black people, does it make sense to demand affirmative action in hostile or dogmatic terms? Obviously it would be more productive to take a diplomatic and conciliatory approachM that would make at least verbal and symbolic concessions to white people who think that affirmative action discriminates against them. But leftist activists do not take such an approach because it would not satisfy their emotional needs. Helping black people is not their real goal. Instead, race problems serve as an excuse for them to express their own hostility and frustrated need for power. In doing so they actually harm black people, because the activists hostile attitude tM oward the white majority tends to intensify race hatred. 22. If our society had no social problems at all, the leftists would have to INVENT problems in order to provide themselves with an excuse for making 23. We emphasize that the foregoing does not pretend to be an accurate description of everyone who might be considered a leftist. It is only a rough indication of a general tendency of leftism. 24. Psychologists use the term to designate the pM children are trained to think and act as society demands. A person is said to be well socialized if he believes in and obeys the moral code of his society and fits in well as a functioning part of that society. It may seem senseless to say that many leftists are oversocialized, since the leftist is perceived as a rebel. Nevertheless, the position can be defended. Many leftists are not such rebels as they seem. 25. The moral code of our society is so demanding that no oM and act in a completely moral way. For example, we are not supposed to hate anyone, yet almost everyone hates somebody at some time or other, whether he admits it to himself or not. Some people are so highly socialized that the attempt to think, feel and act morally imposes a severe burden on them. In order to avoid feelings of guilt, they continually have to deceive themselves about their own motives and find moral explanations for feelings and actions that in M reality have a non-moral origin. We use the term to describe such people. [2] 26. Oversocialization can lead to low self-esteem, a sense of powerlessness, defeatism, guilt, etc. One of the most important means by which our society socializes children is by making them feel ashamed of behavior or speech that is contrary to society s expectations. If this is overdone, or if a particular child is especially susceptible to such feelings, he ends by feeling ashamed oM f HIMSELF. Moreover the thought and the behavior of the oversocialized person are more restricted by society s expectations than are those of the lightly socialized person. The majority of people engage in a significant amount of naughty behavior. They lie, they commit petty thefts, they break traffic laws, they goof off at work, they hate someone, they say spiteful things or they use some underhanded trick to get ahead of the other guy. The oversocialized person cannot do these thinM does do them he generates in himself a sense of shame and self-hatred. The oversocialized person cannot even experience, without guilt, thoughts or feelings that are contrary to the accepted morality; he cannot think thoughts. And socialization is not just a matter of morality; we are socialized to conform to many norms of behavior that do not fall under the heading of morality. Thus the oversocialized person is kept on a psychological leash and spends hM is life running on rails that society has laid down for him. In many oversocialized people this results in a sense of constraint and powerlessness that can be a severe hardship. We suggest that oversocialization is among the more serious cruelties that human beings inflict on one another. 27. We argue that a very important and influential segment of the modern left is oversocialized and that their oversocialization is of great importance in determining the direction of modern leftism.M Leftists of the oversocialized type tend to be intellectuals or members of the upper-middle class. Notice that university intellectuals [3] constitute the most highly socialized segment of our society and also the most left-wing segment. 28. The leftist of the oversocialized type tries to get off his psychological leash and assert his autonomy by rebelling. But usually he is not strong enough to rebel against the most basic values of society. Generally speaking, the goals of today s leftists are NOT in conflict with the accepted morality. On the contrary, the left takes an accepted moral principle, adopts it as its own, and then accuses mainstream society of violating that principle. Examples: racial equality, equality of the sexes, helping poor people, peace as opposed to war, nonviolence generally, freedom of expression, kindness to animals. More fundamentally, the duty of the individual to serve society and the duty of society to take care of the ual. All these have been deeply rooted values of our society (or at least of its middle and upper classes [4] for a long time. These values are explicitly or implicitly expressed or presupposed in most of the material presented to us by the mainstream communications media and the educational system. Leftists, especially those of the oversocialized type, usually do not rebel against these principles but justify their hostility to society by claiming (with some degree of truth) that sociM ety is not living up to these 29. Here is an illustration of the way in which the oversocialized leftist shows his real attachment to the conventional attitudes of our society while pretending to be in rebellion against it. Many leftists push for affirmative action, for moving black people into high-prestige jobs, for improved education in black schools and more money for such schools; the way of life they regard as a social disgrace. They wanM integrate the black man into the system, make him a business executive, a lawyer, a scientist just like upper-middle-class white people. The leftists will reply that the last thing they want is to make the black man into a copy of the white man; instead, they want to preserve African American culture. But in what does this preservation of African American culture consist? It can hardly consist in anything more than eating black-style food, listening to black-style music, wearM clothing and going to a black- style church or mosque. In other words, it can express itself only in superficial matters. In all ESSENTIAL respects most leftists of the oversocialized type want to make the black man conform to white, middle-class ideals. They want to make him study technical subjects, become an executive or a scientist, spend his life climbing the status ladder to prove that black people are as good as white. They want to make black fathers they want black gangs to become nonviolent, etc. But these are exactly the values of the industrial-technological system. The system couldn t care less what kind of music a man listens to, what kind of clothes he wears or what religion he believes in as long as he studies in school, holds a respectable job, climbs the status ladder, is a parent, is nonviolent and so forth. In effect, however much he may deny it, the oversocialized leftist wants to ntegrate the black man into the system and make him adopt its values. 30. We certainly do not claim that leftists, even of the oversocialized type, NEVER rebel against the fundamental values of our society. Clearly they sometimes do. Some oversocialized leftists have gone so far as to rebel against one of modern society s most important principles by engaging in physical violence. By their own account, violence is for them a form of In other words, by committing violencM e they break through the psychological restraints that have been trained into them. Because they are oversocialized these restraints have been more confining for them than for others; hence their need to break free of them. But they usually justify their rebellion in terms of mainstream values. If they engage in violence they claim to be fighting against racism or the like. 31. We realize that many objections could be raised to the foregoing thumbnail sketch of leftist psychology. TheM real situation is complex, and anything like a complete description of it would take several volumes even if the necessary data were available. We claim only to have indicated very roughly the two most important tendencies in the psychology of modern leftism. 32. The problems of the leftist are indicative of the problems of our society as a whole. Low self-esteem, depressive tendencies and defeatism are not restricted to the left. Though they are especially noticeable in the left, ey are widespread in our society. And today s society tries to socialize us to a greater extent than any previous society. We are even told by experts how to eat, how to exercise, how to make love, how to raise our kids 33. Human beings have a need (probably based in biology) for something that we This is closely related to the need for power (which is widely recognized) but is not quite the same thing. The power ocess has four elements. The three most clear-cut of these we call goal, effort and attainment of goal. (Everyone needs to have goals whose attainment requires effort, and needs to succeed in attaining at least some of his goals.) The fourth element is more difficult to define and may not be necessary for everyone. We call it autonomy and will discuss it later (paragraphs 42-44). 34. Consider the hypothetical case of a man who can have anything he wants just by wishing for it. Such a M man has power, but he will develop serious psychological problems. At first he will have a lot of fun, but by and by he will become acutely bored and demoralized. Eventually he may become clinically depressed. History shows that leisured aristocracies tend to become decadent. This is not true of fighting aristocracies that have to struggle to maintain their power. But leisured, secure aristocracies that have no need to exert themselves usually become bored, hedonistic and zed, even though they have power. This shows that power is not enough. One must have goals toward which to exercise one 35. Everyone has goals; if nothing else, to obtain the physical necessities of life: food, water and whatever clothing and shelter are made necessary by the climate. But the leisured aristocrat obtains these things without effort. Hence his boredom and demoralization. 36. Nonattainment of important goals results in death if the goals are physical and in frustration if nonattainment of the goals is compatible with survival. Consistent failure to attain goals throughout life results in defeatism, low self-esteem or depression. 37, Thus, in order to avoid serious psychological problems, a human being needs goals whose attainment requires effort, and he must have a reasonable rate of success in attaining his goals. SURROGATE ACTIVITIES 38. But not every leisured aristocrat becomes bored and demoralized. For example, the emperor HirohM ito, instead of sinking into decadent hedonism, devoted himself to marine biology, a field in which he became distinguished. When people do not have to exert themselves to satisfy their physical needs they often set up artificial goals for themselves. In many cases they then pursue these goals with the same energy and emotional involvement that they otherwise would have put into the search for physical necessities. Thus the aristocrats of the Roman Empire had their literary pretensionsM European aristocrats a few centuries ago invested tremendous time and energy in hunting, though they certainly didn t need the meat; other aristocracies have competed for status through elaborate displays of wealth; and a few aristocrats, like Hirohito, have turned to science. 39. We use the term to designate an activity that is directed toward an artificial goal that people set up for themselves merely in order to have some goal to work toward, or leM t us say, merely for the that they get from pursuing the goal. Here is a rule of thumb for the identification of surrogate activities. Given a person who devotes much time and energy to the pursuit of goal X, ask yourself this: If he had to devote most of his time and energy to satisfying his biological needs, and if that effort required him to use his physical and mental faculties in a varied and interesting way, would he feel seriously deprived becaM use he did not attain goal X? If the answer is no, s pursuit of goal X is a surrogate activity. Hirohito studies in marine biology clearly constituted a surrogate activity, since it is pretty certain that if Hirohito had had to spend his time working at interesting non-scientific tasks in order to obtain the necessities of life, he would not have felt deprived because he didn t know all about the anatomy and life-cycles of marine animals. On the other hand the pM love (for example) is not a surrogate activity, because most people, even if their existence were otherwise satisfactory, would feel deprived if they passed their lives without ever having a relationship with a member of the opposite sex. (But pursuit of an excessive amount of sex, more than one really needs, can be a surrogate activity.) 40. In modern industrial society only minimal effort is necessary to satisfy s physical needs. It is enough to go through a M training program to acquire some petty technical skill, then come to work on time and exert the very modest effort needed to hold a job. The only requirements are a moderate amount of intelligence and, most of all, simple OBEDIENCE. If one has those, society takes care of one from cradle to grave. (Yes, there is an underclass that cannot take the physical necessities for granted, but we are speaking here of mainstream society.) Thus it is not surprising that modern society is fullM of surrogate activities. These include scientific work, athletic achievement, humanitarian work, artistic and literary creation, climbing the corporate ladder, acquisition of money and material goods far beyond the point at which they cease to give any additional physical satisfaction, and social activism when it addresses issues that are not important for the activist personally, as in the case of white activists who work for the rights of nonwhite minorities. These are not always PUM surrogate activities, since for many people they may be motivated in part by needs other than the need to have some goal to pursue. Scientific work may be motivated in part by a drive for prestige, artistic creation by a need to express feelings, militant social activism by hostility. But for most people who pursue them, these activities are in large part surrogate activities. For example, the majority of scientists will probably agree that the heir work is more important than the money and prestige they earn. 41. For many if not most people, surrogate activities are less satisfying than the pursuit of real goals (that is, goals that people would want to attain even if their need for the power process were already fulfilled). One indication of this is the fact that, in many or most cases, people who are deeply involved in surrogate activities are never satisfied, never at rest. Thus the money-maker constantly strives for morM e and more wealth. The scientist no sooner solves one problem than he moves on to the next. The long-distance runner drives himself to run always farther and faster. Many people who pursue surrogate activities will say that they get far more fulfillment from these activities than they do from the of satisfying their biological needs, but that is because in our society the effort needed to satisfy the biological needs has been reduced to triviality. More impoM rtantly, in our society people do not satisfy their biological needs AUTONOMOUSLY but by functioning as parts of an immense social machine. In contrast, people generally have a great deal of autonomy in pursuing their surrogate activities. 42. Autonomy as a part of the power process may not be necessary for every individual. But most people need a greater or lesser degree of autonomy in working toward their goals. Their efforts must be undertaken on their own initiative and M must be under their own direction and control. Yet most people do not have to exert this initiative, direction and control as single individuals. It is usually enough to act as a member of a SMALL group. Thus if half a dozen people discuss a goal among themselves and make a successful joint effort to attain that goal, their need for the power process will be served. But if they work under rigid orders handed down from above that leave them no room for autonomous decision and initiativeM for the power process will not be served. The same is true when decisions are made on a collective basis if the group making the collective decision is so large that the role of each individual is insignificant. [5] 43. It is true that some individuals seem to have little need for autonomy. Either their drive for power is weak or they satisfy it by identifying themselves with some powerful organization to which they belong. And then there are unthinking, animal typesM who seem to be satisfied with a purely physical sense of power (the good combat soldier, who gets his sense of power by developing fighting skills that he is quite content to use in blind obedience to his superiors). 44. But for most people it is through the power process having a goal, making an AUTONOMOUS effort and attaining the goal that self-esteem, self-confidence and a sense of power are acquired. When one does not have adequate opportunity to go through the power process M the consequences are (depending on the individual and on the way the power process is disrupted) boredom, demoralization, low self-esteem, inferiority feelings, defeatism, depression, anxiety, guilt, frustration, hostility, spouse or child abuse, insatiable hedonism, abnormal sexual behavior, sleep disorders, eating disorders, etc. [6] SOURCES OF SOCIAL PROBLEMS 45. Any of the foregoing symptoms can occur in any society, but in modern industrial society they are present on a massive M to mention that the world today seems to be going crazy. This sort of thing is not normal for human societies. There is good reason to believe that primitive man suffered from less stress and frustration and was better satisfied with his way of life than modern man is. It is true that not all was sweetness and light in primitive societies. Abuse of women was common among the Australian aborigines, transexuality was fairly common among some can Indian tribes. But it does appear that GENERALLY SPEAKING the kinds of problems that we have listed in the preceding paragraph were far less common among primitive peoples than they are in modern society. 46. We attribute the social and psychological problems of modern society to the fact that that society requires people to live under conditions radically different from those under which the human race evolved and to behave in ways that conflict with the patterns of behavior that the M developed while living under the earlier conditions. It is clear from what we have already written that we consider lack of opportunity to properly experience the power process as the most important of the abnormal conditions to which modern society subjects people. But it is not the only one. Before dealing with disruption of the power process as a source of social problems we will discuss some of the other sources. 47. Among the abnormal conditions present in modern indusM excessive density of population, isolation of man from nature, excessive rapidity of social change and the breakdown of natural small-scale communities such as the extended family, the village or the tribe. 48. It is well known that crowding increases stress and aggression. The degree of crowding that exists today and the isolation of man from nature are consequences of technological progress. All pre-industrial societies were predominantly rural. The Industrial RevoM lution vastly increased the size of cities and the proportion of the population that lives in them, and modern agricultural technology has made it possible for the Earth to support a far denser population than it ever did before. (Also, technology exacerbates the effects of crowding because it puts increased disruptive powers in people hands. For example, a variety of noise- making devices: power mowers, radios, motorcycles, etc. If the use of these devices is unrestricted, ople who want peace and quiet are frustrated by the noise. If their use is restricted, people who use the devices are frustrated by the regulations. But if these machines had never been invented there would have been no conflict and no frustration generated by them.) 49. For primitive societies the natural world (which usually changes only slowly) provided a stable framework and therefore a sense of security. In the modern world it is human society that dominates nature rather than the other way around, and modern society changes very rapidly owing to technological change. Thus there is no stable framework. 50. The conservatives are fools: They whine about the decay of traditional values, yet they enthusiastically support technological progress and economic growth. Apparently it never occurs to them that you can rapid, drastic changes in the technology and the economy of a society without causing rapid changes in all other aspects of the society as well, and that such rapid changes inevitably break down traditional values. 51. The breakdown of traditional values to some extent implies the breakdown of the bonds that hold together traditional small-scale social groups. The disintegration of small-scale social groups is also promoted by the fact that modern conditions often require or tempt individuals to move to new locations, separating themselves from their communities. Beyond that, a technological society HAS TO weaken family ties and loM it is to function efficiently. In modern society an individual must be first to the system and only secondarily to a small-scale community, because if the internal loyalties of small-scale communities were stronger than loyalty to the system, such communities would pursue their own advantage at the expense of the system. 52. Suppose that a public official or a corporation executive appoints his cousin, his friend or his co- religionist to a position rathM appointing the person best qualified for the job. He has permitted personal loyalty to supersede his loyalty to the system, and that is both of which are terrible sins in modern society. Would-be industrial societies that have done a poor job of subordinating personal or local loyalties to loyalty to the system are usually very inefficient. (Look at Latin America.) Thus an advanced industrial society can tolerate only those small-scaM le communities that are emasculated, tamed and made into tools of the system. [7] 53. Crowding, rapid change and the breakdown of communities have been widely recognized as sources of social problems. But we do not believe they are enough to account for the extent of the problems that are seen today. 54. A few pre-industrial cities were very large and crowded, yet their inhabitants do not seem to have suffered from psychological problems to the same extent as modern man. In America today M there still are uncrowded rural areas, and we find there the same problems as in urban areas, though the problems tend to be less acute in the rural areas. Thus crowding does not seem to be the decisive factor. 55. On the growing edge of the American frontier during the 19th century, the mobility of the population probably broke down extended families and small-scale social groups to at least the same extent as these are broken down today. In fact, many nuclear families lived by choicM isolation, having no neighbors within several miles, that they belonged to no community at all, yet they do not seem to have developed problems as 56. Furthermore, change in American frontier society was very rapid and deep. A man might be born and raised in a log cabin, outside the reach of law and order and fed largely on wild meat; and by the time he arrived at old age he might be working at a regular job and living in an ordered community with w enforcement. This was a deeper change than that which typically occurs in the life of a modern individual, yet it does not seem to have led to psychological problems. In fact, 19th century American society had an optimistic and self-confident tone, quite unlike that of today 57. The difference, we argue, is that modern man has the sense (largely justified) that change is IMPOSED on him, whereas the 19th century frontiersman had the sense (also largely justified) thaM t he created change himself, by his own choice. Thus a pioneer settled on a piece of land of his own choosing and made it into a farm through his own effort. In those days an entire county might have only a couple of hundred inhabitants and was a far more isolated and autonomous entity than a modern county is. Hence the pioneer farmer participated as a member of a relatively small group in the creation of a new, ordered community. One may well question whether the creation of thisM community was an improvement, but at any rate it satisfied s need for the power process. 58. It would be possible to give other examples of societies in which there has been rapid change and/or lack of close community ties without the kind of massive behavioral aberration that is seen in today s industrial society. We contend that the most important cause of social and psychological problems in modern society is the fact that people have insufficient opportunity to through the power process in a normal way. We don modern society is the only one in which the power process has been disrupted. Probably most if not all civilized societies have interfered with the power process to a greater or lesser extent. But in modern industrial society the problem has become particularly acute. Leftism, at least in its recent (mid- to late-20th century) form, is in part a symptom of deprivation with respect to the power process. OF THE POWER PROCESS IN MODERN SOCIETY 59. We divide human drives into three groups: (1) those drives that can be satisfied with minimal effort; (2) those that can be satisfied but only at the cost of serious effort; (3) those that cannot be adequately satisfied no matter how much effort one makes. The power process is the process of satisfying the drives of the second group. The more drives there are in the third group, the more there is frustration, anger, eventually defeatism, 60. In modern industrial society natural human drives tend to be pushed into the first and third groups, and the second group tends to consist increasingly of artificially created drives. 61. In primitive societies, physical necessities generally fall into group 2: They can be obtained, but only at the cost of serious effort. But modern society tends to guaranty the physical necessities to everyone [9] in exchange for only minimal effort, hence physical needs are pushed into M 1. (There may be disagreement about whether the effort needed to hold a job ; but usually, in lower- to middle- level jobs, whatever effort is required is merely that of OBEDIENCE. You sit or stand where you are told to sit or stand and do what you are told to do in the way you are told to do it. Seldom do you have to exert yourself seriously, and in any case you have hardly any autonomy in work, so that the need for the power process is not . Social needs, such as sex, love and status, often remain in group 2 in modern society, depending on the situation of the individual. [10] But, except for people who have a particularly strong drive for status, the effort required to fulfill the social drives is insufficient to satisfy adequately the need for the power process. 63. So certain artificial needs have been created that fall into group 2, hence serve the need for the power process. Advertising and marketing techniques e been developed that make many people feel they need things that their grandparents never desired or even dreamed of. It requires serious effort to earn enough money to satisfy these artificial needs, hence they fall into group 2. (But see paragraphs 80-82.) Modern man must satisfy his need for the power process largely through pursuit of the artificial needs created by the advertising and marketing industry [11], and through surrogate 64. It seems that for many people, mM aybe the majority, these artificial forms of the power process are insufficient. A theme that appears repeatedly in the writings of the social critics of the second half of the 20th century is the sense of purposelessness that afflicts many people in modern society. (This purposelessness is often called by other names such as middle-class vacuity. ) We suggest that the so-called actually a search for a sense of purpose, often for commitmentM surrogate activity. It may be that existentialism is in large part a response to the purposelessness of modern life. [12] Very widespread in modern society is the search for But we think that for the majority of people an activity whose main goal is fulfillment (that is, a surrogate activity) does not bring completely satisfactory fulfillment. In other words, it does not fully satisfy the need for the power process. (See paragraph 41.) That need cM an be fully satisfied only through activities that have some external goal, such as physical necessities, sex, love, status, 65. Moreover, where goals are pursued through earning money, climbing the status ladder or functioning as part of the system in some other way, most people are not in a position to pursue their goals AUTONOMOUSLY. Most workers are s employee and, as we pointed out in paragraph 61, must spend their days doing what they are told to do M in the way they are told to do it. Even people who are in business for themselves have only limited autonomy. It is a chronic complaint of small-business persons and entrepreneurs that their hands are tied by excessive government regulation. Some of these regulations are doubtless unnecessary, but for the most part government regulations are essential and inevitable parts of our extremely complex society. A large portion of small business today operates on the franchise was reported in the Wall Street Journal a few years ago that many of the franchise-granting companies require applicants for franchises to take a personality test that is designed to EXCLUDE those who have creativity and initiative, because such persons are not sufficiently docile to go along obediently with the franchise system. This excludes from small business many of the people who most need autonomy. 66. Today people live more by virtue of what the system does FOR them or TO them than by virtue of what they do for themselves. And what they do for themselves is done more and more along channels laid down by the system. Opportunities tend to be those that the system provides, the opportunities must be exploited in accord with rules and regulations [13], and techniques prescribed by experts must be followed if there is to be a chance of 67. Thus the power process is disrupted in our society through a deficiency of real goals and a deficiency of autonomM y in the pursuit of goals. But it is also disrupted because of those human drives that fall into group 3: the drives that one cannot adequately satisfy no matter how much effort one makes. One of these drives is the need for security. Our lives depend on decisions made by other people; we have no control over these decisions and usually we do not even know the people who make them. ( in which relatively few people Philip B. Heymann of Harvard Law School, quoted by Anthony Lewis, New York Times, April 21, 1995.) Our lives depend on whether safety standards at a nuclear power plant are properly maintained; on how much pesticide is allowed to get into our food or how much pollution into our air; on how skillful (or incompetent) our doctor is; whether we lose or get a job may depend on decisions made by government economists or corporation executives; and so forth. Most individuals are noM t in a position to secure themselves against these threats to more [than] a very limited extent. The s search for security is therefore frustrated, which leads to a sense of powerlessness. 68. It may be objected that primitive man is physically less secure than modern man, as is shown by his shorter life expectancy; hence modern man suffers from less, not more than the amount of insecurity that is normal for human beings. But psychological security does not closely corresM security. What makes us FEEL secure is not so much objective security as a sense of confidence in our ability to take care of ourselves. Primitive man, threatened by a fierce animal or by hunger, can fight in self-defense or travel in search of food. He has no certainty of success in these efforts, but he is by no means helpless against the things that threaten him. The modern individual on the other hand is threatened by many things against which he is helplessM : nuclear accidents, carcinogens in food, environmental pollution, war, increasing taxes, invasion of his privacy by large organizations, nationwide social or economic phenomena that may disrupt his way of life. 69. It is true that primitive man is powerless against some of the things that threaten him; disease for example. But he can accept the risk of disease stoically. It is part of the nature of things, it is no one it is the fault of some imaginary, impersonal dM emon. But threats to the modern individual tend to be MAN-MADE. They are not the results of chance but are IMPOSED on him by other persons whose decisions he, as an individual, is unable to influence. Consequently he feels frustrated, humiliated and angry. 70. Thus primitive man for the most part has his security in his own hands (either as an individual or as a member of a SMALL group) whereas the security of modern man is in the hands of persons or organizations that are mote or too large for him to be able personally to influence them. So s drive for security tends to fall into groups 1 and 3; in some areas (food, shelter etc.) his security is assured at the cost of only trivial effort, whereas in other areas he CANNOT attain security. (The foregoing greatly simplifies the real situation, but it does indicate in a rough, general way how the condition of modern man differs from that of 71. People have many transitory driveM s or impulses that are necessarily frustrated in modern life, hence fall into group 3. One may become angry, but modern society cannot permit fighting. In many situations it does not even permit verbal aggression. When going somewhere one may be in a hurry, or one may be in a mood to travel slowly, but one generally has no choice but to move with the flow of traffic and obey the traffic signals. One may s work in a different way, but usually one can work only rding to the rules laid down by one s employer. In many other ways as well, modern man is strapped down by a network of rules and regulations (explicit or implicit) that frustrate many of his impulses and thus interfere with the power process. Most of these regulations cannot be dispensed with, because they are necessary for the functioning of industrial 72. Modern society is in certain respects extremely permissive. In matters that are irrelevant to the functioning of the M system we can generally do what we please. We can believe in any religion we like (as long as it does not encourage behavior that is dangerous to the system). We can go to bed with anyone we like (as long as we practice ). We can do anything we like as long as it is UNIMPORTANT. But in all IMPORTANT matters the system tends increasingly to regulate our behavior. 73. Behavior is regulated not only through explicit rules and not only by the government. Control is often exeM rcised through indirect coercion or through psychological pressure or manipulation, and by organizations other than the government, or by the system as a whole. Most large organizations use some form of propaganda [14] to manipulate public attitudes or behavior. Propaganda is not limited to and advertisements, and sometimes it is not even consciously intended as propaganda by the people who make it. For instance, the content of entertainment programming is a powerful M propaganda. An example of indirect coercion: There is no law that says we have to go to work every day and follow our employer s orders. Legally there is nothing to prevent us from going to live in the wild like primitive people or from going into business for ourselves. But in practice there is very little wild country left, and there is room in the economy for only a limited number of small business owners. Hence most of us can survive only 74. We suggest that modern man s obsession with longevity, and with maintaining physical vigor and sexual attractiveness to an advanced age, is a symptom of unfulfillment resulting from deprivation with respect to the power process. also is such a symptom. So is the lack of interest in having children that is fairly common in modern society but almost unheard-of in primitive societies. 75. In primitive societies life is a succession of stages. The needs and purposes of one stage having been fulfilled, there is no particular reluctance about passing on to the next stage. A young man goes through the power process by becoming a hunter, hunting not for sport or for fulfillment but to get meat that is necessary for food. (In young women the process is more complex, with greater emphasis on social power; we won here.) This phase having been successfully passed through, the young man has no reluctance about settling down toM the responsibilities of raising a family. (In contrast, some modern people indefinitely postpone having children because they are too busy seeking some kind of suggest that the fulfillment they need is adequate experience of the power with real goals instead of the artificial goals of surrogate activities.) Again, having successfully raised his children, going through the power process by providing them with the physical necessities, the man feels that his work is done and he is prepared to accept old age (if he survives that long) and death. Many modern people, on the other hand, are disturbed by the prospect of physical deterioration and death, as is shown by the amount of effort they expend trying to maintain their physical condition, appearance and health. We argue that this is due to unfulfillment resulting from the fact that they have never put their physical powers to any practical use, have never gone through tM process using their bodies in a serious way. It is not the primitive man, who has used his body daily for practical purposes, who fears the deterioration of age, but the modern man, who has never had a practical use for his body beyond walking from his car to his house. It is the man whose need for the power process has been satisfied during his life who is best prepared to accept the end of that life. 76. In response to the arguments of this section someone will say, find a way to give people the opportunity to go through the power process. For such people the value of the opportunity is destroyed by the very fact that society gives it to them. What they need is to find or make their own opportunities. As long as the system GIVES them their opportunities it still has them on a leash. To attain autonomy they must get off that leash. HOW SOME PEOPLE ADJUST 77. Not everyone in industrial-technological society suffers from psychological roblems. Some people even profess to be quite satisfied with society as it is. We now discuss some of the reasons why people differ so greatly in their response to modern society. 78. First, there doubtless are differences in the strength of the drive for power. Individuals with a weak drive for power may have relatively little need to go through the power process, or at least relatively little need for autonomy in the power process. These are docile types who would have been plantation darkies in the Old South. (We don t mean to sneer at the of the Old South. To their credit, most of the slaves were NOT content with their servitude. We do sneer at people who ARE content with servitude.) 79. Some people may have some exceptional drive, in pursuing which they satisfy their need for the power process. For example, those who have an unusually strong drive for social status may spend their whole lives climbing the status ladder withM out ever getting bored with that game. 80. People vary in their susceptibility to advertising and marketing techniques. Some are so susceptible that, even if they make a great deal of money, they cannot satisfy their constant craving for the the shiny new toys that the marketing industry dangles before their eyes. So they always feel hard-pressed financially even if their income is large, and their cravings 81. Some people have low susceptibility to advertising and marketiM These are the people who aren t interested in money. Material acquisition does not serve their need for the power process. 82. People who have medium susceptibility to advertising and marketing techniques are able to earn enough money to satisfy their craving for goods and services, but only at the cost of serious effort (putting in overtime, taking a second job, earning promotions, etc.). Thus material acquisition serves their need for the power process. But it does M not necessarily follow that their need is fully satisfied. They may have insufficient autonomy in the power process (their work may consist of following orders) and some of their drives may be frustrated (e.g., security, aggression). (We are guilty of oversimplification in paragraphs 80- 82 because we have assumed that the desire for material acquisition is entirely a creation of the advertising and marketing industry. Of course it s not that simple. [11] 83. Some people partly satiM sfy their need for power by identifying themselves with a powerful organization or mass movement. An individual lacking goals or power joins a movement or an organization, adopts its goals as his own, then works toward those goals. When some of the goals are attained, the individual, even though his personal efforts have played only an insignificant part in the attainment of the goals, feels (through his identification with the movement or organization) as if he had gone through he power process. This phenomenon was exploited by the fascists, nazis and communists. Our society uses it too, though less crudely. Example: Manuel Noriega was an irritant to the U.S. (goal: punish Noriega). The U.S. invaded Panama (effort) and punished Noriega (attainment of goal). Thus the U.S. went through the power process and many Americans, because of their identification with the U.S., experienced the power process vicariously. Hence the widespread public approval of the PanamaM invasion; it gave people a sense of power. [15] We see the same phenomenon in armies, corporations, political parties, humanitarian organizations, religious or ideological movements. In particular, leftist movements tend to attract people who are seeking to satisfy their need for power. But for most people identification with a large organization or a mass movement does not fully satisfy the need 84. Another way in which people satisfy their need for the power process is through surrogate activities. As we explained in paragraphs 38-40, a surrogate activity is an activity that is directed toward an artificial goal that the individual pursues for the sake of the gets from pursuing the goal, not because he needs to attain the goal itself. For instance, there is no practical motive for building enormous muscles, hitting a little ball into a hole or acquiring a complete series of postage stamps. Yet many people in our society M devote themselves with passion to bodybuilding, golf or stamp-collecting. Some people are more than others, and therefore will more readily attach importance to a surrogate activity simply because the people around them treat it as important or because society tells them it is important. That is why some people get very serious about essentially trivial activities such as sports, or bridge, or chess, or arcane scholarly pursuits, whereas others who are more clM ear-sighted never see these things as anything but the surrogate activities that they are, and consequently never attach enough importance to them to satisfy their need for the power process in that way. It only remains to point out that in many cases a person a living is also a surrogate activity. Not a PURE surrogate activity, since part of the motive for the activity is to gain the physical necessities and (for some people) social status and the luxuries that adveM want. But many people put into their work far more effort than is necessary to earn whatever money and status they require, and this extra effort constitutes a surrogate activity. This extra effort, together with the emotional investment that accompanies it, is one of the most potent forces acting toward the continual development and perfecting of the system, with negative consequences for individual freedom (see paragraph 131). Especially, for the most creativeM scientists and engineers, work tends to be largely a surrogate activity. This point is so important that it deserves a separate discussion, which we shall give in a moment (paragraphs 87-92). 85. In this section we have explained how many people in modern society do satisfy their need for the power process to a greater or lesser extent. But we think that for the majority of people the need for the power process is not fully satisfied. In the first place, those who have an insatiable driveM for status, or who get firmly on a surrogate activity, or who identify strongly enough with a movement or organization to satisfy their need for power in that way, are exceptional personalities. Others are not fully satisfied with surrogate activities or by identification with an organization (see paragraphs 41, 64). In the second place, too much control is imposed by the system through explicit regulation or through socialization, which results in a deficiency of auM tonomy, and in frustration due to the impossibility of attaining certain goals and the necessity of restraining too many impulses. 86. But even if most people in industrial-technological society were well satisfied, we (FC) would still be opposed to that form of society, because (among other reasons) we consider it demeaning to fulfill one power process through surrogate activities or through identification with an organization, rather than through pursuit of real goaM THE MOTIVES OF SCIENTISTS 87. Science and technology provide the most important examples of surrogate activities. Some scientists claim that they are motivated by But it is easy to see that neither of these can be the principal motive of most scientists. As for that notion is simply absurd. Most scientists work on highly specialized problems that are not the object of any normal curiosity. For example, is an astronomer, a mathematician or an entomologist curious about the properties of isopropyltrimethylmethane? Of course not. Only a chemist is curious about such a thing, and he is curious about it only because chemistry is his surrogate activity. Is the chemist curious about the appropriate classification of a new species of beetle? No. That question is of interest only to the entomologist, and he is interested in it only because entomology is his surrogate activity. If the chemist andM the entomologist had to exert themselves seriously to obtain the physical necessities, and if that effort exercised their abilities in an interesting way but in some nonscientific pursuit, then they wouldn t give a damn about isopropyltrimethylmethane or the classification of beetles. Suppose that lack of funds for postgraduate education had led the chemist to become an insurance broker instead of a chemist. In that case he would have been very interested in insurance ut would have cared nothing about isopropyltrimethylmethane. In any case it is not normal to put into the satisfaction of mere curiosity the amount of time and effort that scientists put into their work. The explanation for the scientists t work any better. Some scientific work has no conceivable relation to the welfare of the human most of archaeology or comparative linguistics M for example. Some other areas of science present obviously dangerous possibilities. Yet scientists in these areas are just as enthusiastic about their work as those who develop vaccines or study air pollution. Consider the case of Dr. Edward Teller, who had an obvious emotional involvement in promoting nuclear power plants. Did this involvement stem from a desire to benefit humanity? If so, t Dr. Teller get emotional about other such a humanitarian then why did he help to develop the H- bomb? As with many other scientific achievements, it is very much open to question whether nuclear power plants actually do benefit humanity. Does the cheap electricity outweigh the accumulating waste and the risk of accidents? Dr. Teller saw only one side of the question. Clearly his emotional involvement with nuclear power arose not from a desire to a personal fulfillment he got from his work M and from seeing it put to 89. The same is true of scientists generally. With possible rare exceptions, their motive is neither curiosity nor a desire to benefit humanity but the need to go through the power process: to have a goal (a scientific problem to solve), to make an effort (research) and to attain the goal (solution of the problem.) Science is a surrogate activity because scientists work mainly for the fulfillment they get out of the work itself. s not that simple. Other motives do play a role for many scientists. Money and status for example. Some scientists may be persons of the type who have an insatiable drive for status (see paragraph 79) and this may provide much of the motivation for their work. No doubt the majority of scientists, like the majority of the general population, are more or less susceptible to advertising and marketing techniques and need money to satisfy their craving for goods and services. Thus sciM surrogate activity. But it is in large part a surrogate activity. 91. Also, science and technology constitute a power mass movement, and many scientists gratify their need for power through identification with this mass movement (see paragraph 83). 92. Thus science marches on blindly, without regard to the real welfare of the human race or to any other standard, obedient only to the psychological needs of the scientists and of the government officials and corporation executives who provide the funds for research. THE NATURE OF FREEDOM 93. We are going to argue that industrial-technological society cannot be reformed in such a way as to prevent it from progressively narrowing the sphere of human freedom. But, because is a word that can be interpreted in many ways, we must first make clear what kind of freedom we are concerned with. we mean the opportunity to go through the power process, with real goals not the arM tificial goals of surrogate activities, and without interference, manipulation or supervision from anyone, especially from any large organization. Freedom means being in control (either as an individual or as a member of a SMALL group) of the life-and-death issues of one existence; food, clothing, shelter and defense against whatever threats there may be in one s environment. Freedom means having power; not the power to control other people but the power to control the circumstanM own life. One does not have freedom if anyone else (especially a large organization) has power over one, no matter how benevolently, tolerantly and permissively that power may be exercised. It is important not to confuse freedom with mere permissiveness (see paragraph 72). 95. It is said that we live in a free society because we have a certain number of constitutionally guaranteed rights. But these are not as important as they seem. The degree of personal freedom that eM xists in a society is determined more by the economic and technological structure of the society than by its laws or its form of government. [16] Most of the Indian nations of New England were monarchies, and many of the cities of the Italian Renaissance were controlled by dictators. But in reading about these societies one gets the impression that they allowed far more personal freedom than our society does. In part this was because they lacked efficient mechanisms for enforcing M s will: There were no modern, well-organized police forces, no rapid long-distance communications, no surveillance cameras, no dossiers of information about the lives of average citizens. Hence it was relatively easy to evade control. 96. As for our constitutional rights, consider for example that of freedom of the press. We certainly don t mean to knock that right; it is very important tool for limiting concentration of political power and for keeping those who political power in line by publicly exposing any misbehavior on their part. But freedom of the press is of very little use to the average citizen as an individual. The mass media are mostly under the control of large organizations that are integrated into the system. Anyone who has a little money can have something printed, or can distribute it on the Internet or in some such way, but what he has to say will be swamped by the vast volume of material put out by the media, hence it will M practical effect. To make an impression on society with words is therefore almost impossible for most individuals and small groups. Take us (FC) for example. If we had never done anything violent and had submitted the present writings to a publisher, they probably would not have been accepted. If they had been been accepted and published, they probably would not have attracted many readers, because it s more fun to watch the entertainment put out by the media than to reaM d a sober essay. Even if these writings had had many readers, most of these readers would soon have forgotten what they had read as their minds were flooded by the mass of material to which the media expose them. In order to get our message before the public with some chance of making a lasting impression, we ve had to kill people. 97. Constitutional rights are useful up to a point, but they do not serve to guarantee much more than what might be called the bourgeois conception of eedom. According to the bourgeois conception, a man is essentially an element of a social machine and has only a certain set of prescribed and delimited freedoms; freedoms that are designed to serve the needs of the social machine more than those of the individual. Thus the bourgeois man has economic freedom because that promotes growth and progress; he has freedom of the press because public criticism restrains misbehavior by political leaders; he has a rightM to a fair trial because imprisonment at the whim of the powerful would be bad for the system. This was clearly the attitude of Simon Bolivar. To him, people deserved liberty only if they used it to promote progress (progress as conceived by the bourgeois). Other bourgeois thinkers have taken a similar view of freedom as a mere means to collective ends. Chester C. Tan, Chinese Political Thought in the Twentieth page 202, explains the philosophy of the Kuomintang leader HM An individual is granted rights because he is a member of society and his community life requires such rights. By community Hu meant the whole society of the nation. And on page 259 Tan states that according to Carsum Chang (Chang Chun-mai, head of the State Socialist Party in China) freedom had to be used in the interest of the state and of the people as a whole. But what kind of freedom does one have if one can use it only as someone else prescribes? FC eption of freedom is not that of Bolivar, Hu, Chang or other bourgeois theorists. The trouble with such theorists is that they have made the development and application of social theories their surrogate activity. Consequently the theories are designed to serve the needs of the theorists more than the needs of any people who may be unlucky enough to live in a society on which the theories are imposed. 98. One more point to be made in this section: It should not be assumed that n has enough freedom just because he SAYS he has enough. Freedom is restricted in part by psychological controls of which people are unconscious, and moreover many people s ideas of what constitutes freedom are governed more by social convention than by their real needs. For s likely that many leftists of the oversocialized type would say that most people, including themselves, are socialized too little rather than too much, yet the oversocialized leftist pays a heavy pM price for his high level of socialization. SOME PRINCIPLES OF HISTORY 99. Think of history as being the sum of two components: an erratic component that consists of unpredictable events that follow no discernible pattern, and a regular component that consists of long-term historical trends. Here we are concerned with the long-term trends. 100. FIRST PRINCIPLE. If a SMALL change is made that affects a long-term historical trend, then the effect of that change will almost alwM the trend will soon revert to its original state. (Example: A reform movement designed to clean up political corruption in a society rarely has more than a short-term effect; sooner or later the reformers relax and corruption creeps back in. The level of political corruption in a given society tends to remain constant, or to change only slowly with the evolution of the society. Normally, a political cleanup will be permanent only if accompanied by widespM read social changes; a SMALL change in the t be enough.) If a small change in a long-term historical trend appears to be permanent, it is only because the change acts in the direction in which the trend is already moving, so that the trend is not altered by only pushed a step ahead. 101. The first principle is almost a tautology. If a trend were not stable with respect to small changes, it would wander at random rather than following a definite direction; in other M words it would not be a long- term trend at 102. SECOND PRINCIPLE. If a change is made that is sufficiently large to alter permanently a long-term historical trend, then it will alter the society as a whole. In other words, a society is a system in which all parts are interrelated, and you can t permanently change any important part without changing all other parts as well. 103. THIRD PRINCIPLE. If a change is made that is large enough to alter permanently a long-term trM end, then the consequences for the society as a whole cannot be predicted in advance. (Unless various other societies have passed through the same change and have all experienced the same consequences, in which case one can predict on empirical grounds that another society that passes through the same change will be like to experience similar consequences.) 104. FOURTH PRINCIPLE. A new kind of society cannot be designed on paper. That is, you cannot plan out a new form of societM y in advance, then set it up and expect it to function as it was designed to do. 105. The third and fourth principles result from the complexity of human societies. A change in human behavior will affect the economy of a society and its physical environment; the economy will affect the environment and vice versa, and the changes in the economy and the environment will affect human behavior in complex, unpredictable ways; and so forth. The network of causes and effects is far tooM complex to be untangled and understood. 106. FIFTH PRINCIPLE. People do not consciously and rationally choose the form of their society. Societies develop through processes of social evolution that are not under rational human control. 107. The fifth principle is a consequence of the other four. 108. To illustrate: By the first principle, generally speaking an attempt at social reform either acts in the direction in which the society is developing anyway (so that it merely accelerates a M change that would have occurred in any case) or else it has only a transitory effect, so that the society soon slips back into its old groove. To make a lasting change in the direction of development of any important aspect of a society, reform is insufficient and revolution is required. (A revolution does not necessarily involve an armed uprising or the overthrow of a government.) By the second principle, a revolution never changes only one aspect of a society, it changes M the whole society; and by the third principle changes occur that were never expected or desired by the revolutionaries. By the fourth principle, when revolutionaries or utopians set up a new kind of society, it never works out as planned. 109. The American Revolution does not provide a counterexample. The American was not a revolution in our sense of the word, but a war of independence followed by a rather far-reaching political reform. The Founding Fathers did M not change the direction of development of American society, nor did they aspire to do so. They only freed the development of American society from the retarding effect of British rule. Their political reform did not change any basic trend, but only pushed American political culture along its natural direction of development. British society, of which American society was an offshoot, had been moving for a long time in the direction of representative democracy. And prior to the WM Independence the Americans were already practicing a significant degree of representative democracy in the colonial assemblies. The political system established by the Constitution was modeled on the British system and on the colonial assemblies. With major alteration, to be sure doubt that the Founding Fathers took a very important step. But it was a step along the road that English-speaking world was already traveling. The proof is that Britain and allM of its colonies that were populated predominantly by people of British descent ended up with systems of representative democracy essentially similar to that of the United States. If the Founding Fathers had lost their nerve and declined to sign the Declaration of Independence, our way of life today would not have been significantly different. Maybe we would have had somewhat closer ties to Britain, and would have had a Parliament and Prime Minister instead of d President. No big deal. Thus the American Revolution provides not a counterexample to our principles but a good illustration of 110. Still, one has to use common sense in applying the principles. They are expressed in imprecise language that allows latitude for interpretation, and exceptions to them can be found. So we present these principles not as inviolable laws but as rules of thumb, or guides to thinking, that may provide a partial antidote to naive ideas about theM future of society. The principles should be borne constantly in mind, and whenever one reaches a conclusion that conflicts with them one should carefully reexamine one thinking and retain the conclusion only if one has good, solid reasons for INDUSTRIAL-TECHNOLOGICAL SOCIETY CANNOT BE REFORMED 111. The foregoing principles help to show how hopelessly difficult it would be to reform the industrial system in such a way as to prevent it from progressively narrowM ing our sphere of freedom. There has been a consistent tendency, going back at least to the Industrial Revolution for technology to strengthen the system at a high cost in individual freedom and local autonomy. Hence any change designed to protect freedom from technology would be contrary to a fundamental trend in the development of our society. Consequently, such a change either would be a transitory one by the tide of history or, if large enough to be permanentM nature of our whole society. This by the first and second principles. Moreover, since society would be altered in a way that could not be predicted in advance (third principle) there would be great risk. Changes large enough to make a lasting difference in favor of freedom would not be initiated because it would be realized that they would gravely disrupt the system. So any attempts at reform would be too timid to be effective. Even if changes large enough M to make a lasting difference were initiated, they would be retracted when their disruptive effects became apparent. Thus, permanent changes in favor of freedom could be brought about only by persons prepared to accept radical, dangerous and unpredictable alteration of the entire system. In other words by revolutionaries, not reformers. 112. People anxious to rescue freedom without sacrificing the supposed benefits of technology will suggest naive schemes for some new form of society tM would reconcile freedom with technology. Apart from the fact that people who make such suggestions seldom propose any practical means by which the new form of society could be set up in the first place, it follows from the fourth principle that even if the new form of society could be once established, it either would collapse or would give results very different from those expected. 113. So even on very general grounds it seems highly improbable that any way of ng society could be found that would reconcile freedom with modern technology. In the next few sections we will give more specific reasons for concluding that freedom and technological progress are incompatible. RESTRICTION OF FREEDOM IS UNAVOIDABLE IN INDUSTRIAL SOCIETY 114. As explained in paragraphs 65-67, 70-73, modern man is strapped down by a network of rules and regulations, and his fate depends on the actions of persons remote from him whose decisions he cannot influence. This is nM accidental or a result of the arbitrariness of arrogant bureaucrats. It is necessary and inevitable in any technologically advanced society. The system HAS TO regulate human behavior closely in order to function. At work people have to do what they are told to do, otherwise production would be thrown into chaos. Bureaucracies HAVE TO be run according to rigid rules. To allow any substantial personal discretion to lower-level bureaucrats would disrupt the system and lead M to charges of unfairness due to differences in the way individual bureaucrats exercised their discretion. It is true that some restrictions on our freedom could be eliminated, but GENERALLY SPEAKING the regulation of our lives by large organizations is necessary for the functioning of industrial-technological society. The result is a sense of powerlessness on the part of the average person. It may be, however, that formal regulations will tend increasingly to be psychological tools that make us want to do what the system requires of us. (Propaganda [14], educational techniques, programs, etc.) 115. The system HAS TO force people to behave in ways that are increasingly remote from the natural pattern of human behavior. For example, the system needs scientists, mathematicians and engineers. It can them. So heavy pressure is put on children to excel in these fields. It t natural for an adolesM cent human being to spend the bulk of his time sitting at a desk absorbed in study. A normal adolescent wants to spend his time in active contact with the real world. Among primitive peoples the things that children are trained to do tend to be in reasonable harmony with natural human impulses. Among the American Indians, for example, boys were trained in active outdoor pursuits just the sort of thing that boys like. But in our society children are pushed into studying technicaM subjects, which most do grudgingly. 116. Because of the constant pressure that the system exerts to modify human behavior, there is a gradual increase in the number of people who cannot or will not adjust to society s requirements: welfare leeches, youth-gang members, cultists, anti-government rebels, radical environmentalist saboteurs, dropouts and resisters of various kinds. 117. In any technologically advanced society the individual on decisions thatM he personally cannot influence to any great extent. A technological society cannot be broken down into small, autonomous communities, because production depends on the cooperation of very large numbers of people and machines. Such a society MUST be highly organized and decisions HAVE TO be made that affect very large numbers of people. When a decision affects, say, a million people, then each of the affected individuals has, on the average, only a one-millionth share in making tM decision. What usually happens in practice is that decisions are made by public officials or corporation executives, or by technical specialists, but even when the public votes on a decision the number of voters ordinarily is too large for the vote of any one individual to be significant. [17] Thus most individuals are unable to influence measurably the major decisions that affect their lives. There is no conceivable way to remedy this in a technologically advanced socieM ty. The system tries to this problem by using propaganda to make people WANT the decisions that have been made for them, but even if this successful in making people feel better, it would be demeaning. 118. Conservatives and some others advocate more communities once did have autonomy, but such autonomy becomes less and less possible as local communities become more enmeshed with and dependent on systems like public utilities, computer networks, highway systems, the mass communications media, the modern health care system. Also operating against autonomy is the fact that technology applied in one location often affects people at other locations far way. Thus pesticide or chemical use near a creek may contaminate the water supply hundreds of miles downstream, and the greenhouse effect affects the whole world. 119. The system does not and cannot exist to satisfy human needs. InsM human behavior that has to be modified to fit the needs of the system. This has nothing to do with the political or social ideology that may pretend to guide the technological system. It is the fault of technology, because the system is guided not by ideology but by technical necessity. [18] Of course the system does satisfy many human needs, but generally speaking it does this only to the extend that it is to the advantage of the system to do it. It is the needM s of the system that are paramount, not those of the human being. For example, the system provides people with food because the system t function if everyone starved; it attends to people needs whenever it can CONVENIENTLY do so, because it couldn too many people became depressed or rebellious. But the system, for good, solid, practical reasons, must exert constant pressure on people to mold their behavior to the needs of the system. To M much waste accumulating? The government, the media, the educational system, environmentalists, everyone inundates us with a mass of propaganda about recycling. Need more technical personnel? A chorus of voices exhorts kids to study science. No one stops to ask whether it is inhumane to force adolescents to spend the bulk of their time studying subjects most of them hate. When skilled workers are put out of a job by technical advances and have to undergo one asks whether it is humiliating for them to be pushed around in this way. It is simply taken for granted that everyone must bow to technical necessity. and for good reason: If human needs were put before technical necessity there would be economic problems, unemployment, shortages or worse. The concept of in our society is defined largely by the extent to which an individual behaves in accord with the needs of the system and does so without showing signs oM 120. Efforts to make room for a sense of purpose and for autonomy within the system are no better than a joke. For example, one company, instead of having each of its employees assemble only one section of a catalogue, had each assemble a whole catalogue, and this was supposed to give them a sense of purpose and achievement. Some companies have tried to give their employees more autonomy in their work, but for practical reasons this usually can be done only to a very lM imited extent, and in any case employees are never given autonomy as to ultimate goals efforts can never be directed toward goals that they select personally, but only toward their employer s goals, such as the survival and growth of the company. Any company would soon go out of business if it permitted its employees to act otherwise. Similarly, in any enterprise within a socialist system, workers must direct their efforts toward the goals of the enterprise, otherwise the enterprise will not serve its purpose as part of the system. Once again, for purely technical reasons it is not possible for most individuals or small groups to have much autonomy in industrial society. Even the small-business owner commonly has only limited autonomy. Apart from the necessity of government regulation, he is restricted by the fact that he must fit into the economic system and conform to its requirements. For instance, when someone developM s a new technology, the small-business person often has to use that technology whether he wants to or not, in order to remain competitive. PARTS OF TECHNOLOGY CANNOT BE SEPARATED FROM THE 121. A further reason why industrial society cannot be reformed in favor of freedom is that modern technology is a unified system in which all parts are dependent on one another. You can technology and retain only the rts. Take modern medicine, for example. Progress in medical science depends on progress in chemistry, physics, biology, computer science and other fields. Advanced medical treatments require expensive, high-tech equipment that can be made available only by a technologically progressive, economically rich society. Clearly you can t have much progress in medicine without the whole technological system and everything that goes with it. 122. Even if medical progress could be maintM ained without the rest of the technological system, it would by itself bring certain evils. Suppose for example that a cure for diabetes is discovered. People with a genetic tendency to diabetes will then be able to survive and reproduce as well as anyone else. Natural selection against genes for diabetes will cease and such genes will spread throughout the population. (This may be occurring to some extent already, since diabetes, while not curable, can be controlled h use of insulin.) The same thing will happen with many other diseases susceptibility to which is affected by genetic degradation of the population. The only solution will be some sort of eugenics program or extensive genetic engineering of human beings, so that man in the future will no longer be a creation of nature, or of chance, or of God (depending on your religious or philosophical opinions), but a manufactured product. 123. If you think that big government interferes in your liM fe too much NOW, just wait till the government starts regulating the genetic constitution of your children. Such regulation will inevitably follow the introduction of genetic engineering of human beings, because the consequences of unregulated genetic engineering would be disastrous. [19] 124. The usual response to such concerns is to talk about a code of ethics would not serve to protect freedom in the face of medical progress; it would only make matteM rs worse. A code of ethics applicable to genetic engineering would be in effect a means of regulating the genetic constitution of human beings. Somebody (probably the upper-middle class, mostly) would decide that such and such applications of genetic engineering and others were not, so that in effect they would be imposing their own values on the genetic constitution of the population at large. Even if a code of ethics were chosen on a completely democratic basis, the majority would be imposing their own values on any minorities who might have a different idea of what constituted an genetic engineering. The only code of ethics that would truly protect freedom would be one that prohibited ANY genetic engineering of human beings, and you can be sure that no such code will ever be applied in a technological society. No code that reduced genetic engineering to a minor role could stand up for long, because the temM ptation presented by the immense power of biotechnology would be irresistible, especially since to the majority of people many of its applications will seem obviously and unequivocally good (eliminating physical and mental diseases, giving people the abilities they need to get along in today s world). Inevitably, genetic engineering will be used extensively, but only in ways consistent with the needs of the industrial- technological system. [20] TECHNOLOGY IS A MORE POWERFUL SM OCIAL FORCE THAN THE ASPIRATION FOR FREEDOM 125. It is not possible to make a LASTING compromise between technology and freedom, because technology is by far the more powerful social force and continually encroaches on freedom through REPEATED compromises. Imagine the case of two neighbors, each of whom at the outset owns the same amount of land, but one of whom is more powerful than the other. The powerful one demands a piece of the other s land. The weak one refuses. The powerful M s compromise. Give me half of what I asked. has little choice but to give in. Some time later the powerful neighbor demands another piece of land, again there is a compromise, and so forth. By forcing a long series of compromises on the weaker man, the powerful one eventually gets all of his land. So it goes in the conflict between technology and freedom. 126. Let us explain why technology is a more powerful social force than the 127. A technological advance that appears not to threaten freedom often turns out to threaten it very seriously later on. For example, consider motorized transport. A walking man formerly could go where he pleased, go at his own pace without observing any traffic regulations, and was independent of technological support-systems. When motor vehicles were introduced they appeared to increase man s freedom. They took no freedom away from the walking man, no onM e had to have an automobile if he didn anyone who did choose to buy an automobile could travel much faster and farther than a walking man. But the introduction of motorized transport soon changed society in such a way as to restrict greatly man locomotion. When automobiles became numerous, it became necessary to regulate their use extensively. In a car, especially in densely populated areas, one cannot just go where one likes at one is governed by the flow of traffic and by various traffic laws. One is tied down by various obligations: license requirements, driver test, renewing registration, insurance, maintenance required for safety, monthly payments on purchase price. Moreover, the use of motorized transport is no longer optional. Since the introduction of motorized transport the arrangement of our cities has changed in such a way that the majority of people no longer live within walM king distance of their place of employment, shopping areas and recreational opportunities, so that they HAVE TO depend on the automobile for transportation. Or else they must use public transportation, in which case they have even less control over their own movement than when driving a car. Even the walker s freedom is now greatly restricted. In the city he continually has to stop to wait for traffic lights that are designed mainly to serve auto traffic. In the country, motor M it dangerous and unpleasant to walk along the highway. (Note this important point that we have just illustrated with the case of motorized transport: When a new item of technology is introduced as an option that an individual can accept or not as he chooses, it does not necessarily REMAIN optional. In many cases the new technology changes society in such a way that people eventually find themselves FORCED to use it.) 128. While technological progress AS A WHOLE conM tinually narrows our sphere of freedom, each new technical advance CONSIDERED BY ITSELF appears to be desirable. Electricity, indoor plumbing, rapid long-distance communications ... how could one argue against any of these things, or against any other of the innumerable technical advances that have made modern society? It would have been absurd to resist the introduction of the telephone, for example. It offered many advantages and no disadvantages. Yet, as we paragraphs 59-76, all these technical advances taken together have created a world in which the average man s fate is no longer in his own hands or in the hands of his neighbors and friends, but in those of politicians, corporation executives and remote, anonymous technicians and bureaucrats whom he as an individual has no power to influence. [21] The same process will continue in the future. Take genetic engineering, for example. Few people will resist the introduction of a geM that eliminates a hereditary disease. It does no apparent harm and prevents much suffering. Yet a large number of genetic improvements taken together will make the human being into an engineered product rather than a free creation of chance (or of God, or whatever, depending on your religious 129. Another reason why technology is such a powerful social force is that, within the context of a given society, technological progress marches in direction; it can never be reversed. Once a technical innovation has been introduced, people usually become dependent on it, so that they can never again do without it, unless it is replaced by some still more advanced innovation. Not only do people become dependent as individuals on a new item of technology, but, even more, the system as a whole becomes dependent on it. (Imagine what would happen to the system today if computers, for example, were eliminated.) Thus the system cM one direction, toward greater technologization. Technology repeatedly forces freedom to take a step back, but technology can never take a step short of the overthrow of the whole technological system. 130. Technology advances with great rapidity and threatens freedom at many different points at the same time (crowding, rules and regulations, increasing dependence of individuals on large organizations, propaganda and other psychological techniques, genetic M engineering, invasion of privacy through surveillance devices and computers, etc.). To hold back any ONE of the threats to freedom would require a long and difficult social struggle. Those who want to protect freedom are overwhelmed by the sheer number of new attacks and the rapidity with which they develop, hence they become apathetic and no longer resist. To fight each of the threats separately would be futile. Success can be hoped for only by fighting the technological sM ystem as a whole; but that is revolution, not reform. 131. Technicians (we use this term in its broad sense to describe all those who perform a specialized task that requires training) tend to be so involved in their work (their surrogate activity) that when a conflict arises between their technical work and freedom, they almost always decide in favor of their technical work. This is obvious in the case of scientists, but it also appears elsewhere: Educators, humanitarian groups, consM organizations do not hesitate to use propaganda or other psychological techniques to help them achieve their laudable ends. Corporations and government agencies, when they find it useful, do not hesitate to collect information about individuals without regard to their privacy. Law enforcement agencies are frequently inconvenienced by the constitutional rights of suspects and often of completely innocent persons, and they do whatever they can do legally (or sometimeM s illegally) to restrict or circumvent those rights. Most of these educators, government officials and law officers believe in freedom, privacy and constitutional rights, but when these conflict with their work, they usually feel that their work is more important. 132. It is well known that people generally work better and more persistently when striving for a reward than when attempting to avoid a punishment or negative outcome. Scientists and other technicians are motivated maM the rewards they get through their work. But those who oppose technological invasions of freedom are working to avoid a negative outcome, consequently there are few who work persistently and well at this discouraging task. If reformers ever achieved a signal victory that seemed to set up a solid barrier against further erosion of freedom through technical progress, most would tend to relax and turn their attention to more agreeable pursuits. But the scientists wouldM remain busy in their laboratories, and technology as it progresses would find ways, in spite of any barriers, to exert more and more control over individuals and make them always more dependent on 133. No social arrangements, whether laws, institutions, customs or ethical codes, can provide permanent protection against technology. History shows that all social arrangements are transitory; they all change or break down eventually. But technological advances are permaM nent within the context of a given civilization. Suppose for example that it were possible to arrive at some social arrangements that would prevent genetic engineering from being applied to human beings, or prevent it from being applied in such a way as to threaten freedom and dignity. Still, the technology would remain waiting. Sooner or later the social arrangement would break down. Probably sooner, given the pace of change in our society. Then genetic engineering would bM egin to invade our sphere of freedom, and this invasion would be irreversible (short of a breakdown of technological civilization itself). Any illusions about achieving anything permanent through social arrangements should be dispelled by what is currently happening with environmental legislation. A few years ago its seemed that there were secure legal barriers preventing at least SOME of the worst forms of environmental degradation. A change in the political wind, and those barriers begin to crumble. 134. For all of the foregoing reasons, technology is a more powerful social force than the aspiration for freedom. But this statement requires an important qualification. It appears that during the next several decades the industrial-technological system will be undergoing severe stresses due to economic and environmental problems, and especially due to problems of human behavior (alienation, rebellion, hostility, a variety of social and l difficulties). We hope that the stresses through which the system is likely to pass will cause it to break down, or at least will weaken it sufficiently so that a revolution against it becomes possible. If such a revolution occurs and is successful, then at that particular moment the aspiration for freedom will have proved more powerful than technology. 135. In paragraph 125 we used an analogy of a weak neighbor who is left destitute by a strong neighbor who takes all his land by foM a series of compromises. But suppose now that the strong neighbor gets sick, so that he is unable to defend himself. The weak neighbor can force the strong one to give him his land back, or he can kill him. If he lets the strong man survive and only forces him to give the land back, he is a fool, because when the strong man gets well he will again take all the land for himself. The only sensible alternative for the weaker man is to kill the strong one while he M has the chance. In the same way, while the industrial system is sick we must destroy it. If we compromise with it and let it recover from its sickness, it will eventually wipe out all of our SIMPLER SOCIAL PROBLEMS HAVE PROVED INTRACTABLE 136. If anyone still imagines that it would be possible to reform the system in such a way as to protect freedom from technology, let him consider how clumsily and for the most part unsuccessfully our society has dealt with al problems that are far more simple and straightforward. Among other things, the system has failed to stop environmental degradation, political corruption, drug trafficking or domestic abuse. 137. Take our environmental problems, for example. Here the conflict of values is straightforward: economic expedience now versus saving some of our natural resources for our grandchildren. [22] But on this subject we get only a lot of blather and obfuscation from the people who have power, and M nothing like a clear, consistent line of action, and we keep on piling up environmental problems that our grandchildren will have to live with. Attempts to resolve the environmental issue consist of struggles and compromises between different factions, some of which are ascendant at one moment, others at another moment. The line of struggle changes with the shifting currents of public opinion. This is not a rational process, nor is it one that is likely to lead to a timely M and successful solution to the problem. Major social problems, if they get at all, are rarely or never solved through any rational, comprehensive plan. They just work themselves out through a process in which various competing groups pursuing their own (usually short- term) self-interest [23] arrive (mainly by luck) at some more or less stable modus vivendi. In fact, the principles we formulated in paragraphs 100-106 make it seem doubtful that rational, rm social planning can EVER be successful. 138. Thus it is clear that the human race has at best a very limited capacity for solving even relatively straightforward social problems. How then is it going to solve the far more difficult and subtle problem of reconciling freedom with technology? Technology presents clear-cut material advantages, whereas freedom is an abstraction that means different things to different people, and its loss is easily obscured by propaganda and fancy talk.M 139. And note this important difference: It is conceivable that our environmental problems (for example) may some day be settled through a rational, comprehensive plan, but if this happens it will be only because it is in the long-term interest of the system to solve these problems. But it is NOT in the interest of the system to preserve freedom or small-group autonomy. On the contrary, it is in the interest of the system to bring human behavior under control to the greatest poM ssible extent. [24] Thus, while practical considerations may eventually force the system to take a rational, prudent approach to environmental problems, equally practical considerations will force the system to regulate human behavior ever more closely (preferably by indirect means that will disguise the encroachment on freedom). This isn t just our opinion. Eminent social scientists (e.g. James Q. Wilson) have stressed the importance of REVOLUTION IS EASIER THAN REFORM 140. We hope we have convinced the reader that the system cannot be reformed in such a way as to reconcile freedom with technology. The only way out is to dispense with the industrial-technological system altogether. This implies revolution, not necessarily an armed uprising, but certainly a radical and fundamental change in the nature of society. 141. People tend to assume that because a revolution involves a much greater change than reformM does, it is more difficult to bring about than reform is. Actually, under certain circumstances revolution is much easier than reform. The reason is that a revolutionary movement can inspire an intensity of commitment that a reform movement cannot inspire. A reform movement merely offers to solve a particular social problem. A revolutionary movement offers to solve all problems at one stroke and create a whole new world; it provides the kind of ideal for which people take great risks and make great sacrifices. For this reasons it would be much easier to overthrow the whole technological system than to put effective, permanent restraints on the development or application of any one segment of technology, such as genetic engineering, for example. Not many people will devote themselves with single-minded passion to imposing and maintaining restraints on genetic engineering, but under suitable conditions large numbers of people may devote themselM ves passionately to a revolution against the industrial-technological system. As we noted in paragraph 132, reformers seeking to limit certain aspects of technology would be working to avoid a negative outcome. But revolutionaries work to gain a powerful reward fulfillment of their revolutionary vision therefore work harder and more persistently than reformers do. 142. Reform is always restrained by the fear of painful consequences if changes go too far. But once a revolM utionary fever has taken hold of a society, people are willing to undergo unlimited hardships for the sake of their revolution. This was clearly shown in the French and Russian Revolutions. It may be that in such cases only a minority of the population is really committed to the revolution, but this minority is sufficiently large and active so that it becomes the dominant force in society. We will have more to say about revolution in paragraphs 180-205. CONTROL OF HUMAN BEHAVIORM 143. Since the beginning of civilization, organized societies have had to put pressures on human beings of the sake of the functioning of the social organism. The kinds of pressures vary greatly from one society to another. Some of the pressures are physical (poor diet, excessive labor, environmental pollution), some are psychological (noise, crowding, forcing human behavior into the mold that society requires). In the past, human nature has been approximately constant, or at aM ny rate has varied only within certain bounds. Consequently, societies have been able to push people only up to certain limits. When the limit of human endurance has been passed, things start going wrong: rebellion, or crime, or corruption, or evasion of work, or depression and other mental problems, or an elevated death rate, or a declining birth rate or something else, so that either the society breaks down, or its functioning becomes too inefficient and it is gradually, through conquest, attrition or evolution) replaced by some more efficient form of society. [25] 144. Thus human nature has in the past put certain limits on the development of societies. People could be pushed only so far and no farther. But today this may be changing, because modern technology is developing ways of modifying human beings. 145. Imagine a society that subjects people to conditions that make them terribly unhappy, then gives them drugs to take away their unM Science fiction? It is already happening to some extent in our own society. It is well known that the rate of clinical depression has been greatly increasing in recent decades. We believe that this is due to disruption of the power process, as explained in paragraphs 59-76. But even if we are wrong, the increasing rate of depression is certainly the result of SOME conditions that exist in today s society. Instead of removing the conditions that make people deprM essed, modern society gives them antidepressant drugs. In effect, antidepressants are a means of modifying s internal state in such a way as to enable him to tolerate social conditions that he would otherwise find intolerable. (Yes, we know that depression is often of purely genetic origin. We are referring here to those cases in which environment plays the predominant role.) 146. Drugs that affect the mind are only one example of the new methods of uman behavior that modern society is developing. Let us look at some of the other methods. 147. To start with, there are the techniques of surveillance. Hidden video cameras are now used in most stores and in many other places, computers are used to collect and process vast amounts of information about individuals. Information so obtained greatly increases the effectiveness of physical coercion (i.e., law enforcement). [26] Then there are the methods of propaganda, for which theM mass communication media provide effective vehicles. Efficient techniques have been developed for winning elections, selling products, influencing public opinion. The entertainment industry serves as an important psychological tool of the system, possibly even when it is dishing out large amounts of sex and violence. Entertainment provides modern man with an essential means of escape. While absorbed in television, videos, etc., he can forget stress, anxiety, frustration, dissatiM Many primitive peoples, when they don t have work to do, are quite content to sit for hours at a time doing nothing at all, because they are at peace with themselves and their world. But most modern people must be constantly occupied or entertained, otherwise they get fidgety, uneasy, irritable. 148. Other techniques strike deeper than the foregoing. Education is no longer a simple affair of paddling a kid s behind when he doesn and patting him on the head when he does know them. It is becoming a scientific technique for controlling the child s development. Sylvan Learning Centers, for example, have had great success in motivating children to study, and psychological techniques are also used with more or less success in many conventional schools. techniques that are taught to parents are designed to make children accept fundamental values of the system and behave in wM ays that the system finds desirable. techniques, psychotherapy and so forth are ostensibly designed to benefit individuals, but in practice they usually serve as methods for inducing individuals to think and behave as the system requires. (There is no contradiction here; an individual whose attitudes or behavior bring him into conflict with the system is up against a force that is too powerful for him to conquer or escape from, henM suffer from stress, frustration, defeat. His path will be much easier if he thinks and behaves as the system requires. In that sense the system is acting for the benefit of the individual when it brainwashes him into conformity.) Child abuse in its gross and obvious forms is disapproved in most if not all cultures. Tormenting a child for a trivial reason or no reason at all is something that appalls almost everyone. But many psychologists interpret the coM ncept of abuse much more broadly. Is spanking, when used as part of a rational and consistent system of discipline, a form of abuse? The question will ultimately be decided by whether or not spanking tends to produce behavior that makes a person fit in well with the existing system of society. In practice, the word tends to be interpreted to include any method of child-rearing that produces behavior inconvenient for the system. Thus, when they go beyond ention of obvious, senseless cruelty, programs for preventing are directed toward the control of human behavior on behalf 149. Presumably, research will continue to increase the effectiveness of psychological techniques for controlling human behavior. But we think it is unlikely that psychological techniques alone will be sufficient to adjust human beings to the kind of society that technology is creating. Biological methods probably will have toM be used. We have already mentioned the use of drugs in this connection. Neurology may provide other avenues for modifying the human mind. Genetic engineering of human beings is already beginning to occur in the form of and there is no reason to assume that such methods will not eventually be used to modify those aspects of the body that affect mental functioning. 150. As we mentioned in paragraph 134, industrial society seems likely to be entering a period oM f severe stress, due in part to problems of human behavior and in part to economic and environmental problems. And a considerable proportion of the system s economic and environmental problems result from the way human beings behave. Alienation, low self-esteem, depression, hostility, rebellion; children who won youth gangs, illegal drug use, rape, child abuse, other crimes, unsafe sex, teen pregnancy, population growth, political corruption, race hatred, c rivalry, bitter ideological conflict (e.g., pro-choice vs. pro- life), political extremism, terrorism, sabotage, anti-government groups, hate groups. All these threaten the very survival of the system. The system will therefore be FORCED to use every practical means of controlling human 151. The social disruption that we see today is certainly not the result of mere chance. It can only be a result of the conditions of life that the system imposes on people. (We have M argued that the most important of these conditions is disruption of the power process.) If the systems succeeds in imposing sufficient control over human behavior to assure its own survival, a new watershed in human history will have been passed. Whereas formerly the limits of human endurance have imposed limits on the development of societies (as we explained in paragraphs 143, 144), industrial-technological society will be able to pass those limits by modifying human beinM gs, whether by psychological methods or biological methods or both. In the future, social systems will not be adjusted to suit the needs of human beings. Instead, human being will be adjusted to suit the needs of the system. [27] 152. Generally speaking, technological control over human behavior will probably not be introduced with a totalitarian intention or even through a conscious desire to restrict human freedom. [28] Each new step in the assertion of control over the human M mind will be taken as a rational response to a problem that faces society, such as curing alcoholism, reducing the crime rate or inducing young people to study science and engineering. In many cases there will be a humanitarian justification. For example, when a psychiatrist prescribes an anti-depressant for a depressed patient, he is clearly doing that individual a favor. It would be inhumane to withhold the drug from someone who needs it. When parents send their children to SylM Learning Centers to have them manipulated into becoming enthusiastic about their studies, they do so from concern for their children be that some of these parents wish that one didn t have to have specialized training to get a job and that their kid didn t have to be brainwashed into becoming a computer nerd. But what can they do? They can and their child may be unemployable if he doesn t have certain skills. So 153. Thus control over human behavior will be introduced not by a calculated decision of the authorities but through a process of social evolution (RAPID evolution, however). The process will be impossible to resist, because each advance, considered by itself, will appear to be beneficial, or at least the evil involved in making the advance will appear to be beneficial, or at least the evil involved in making the advance will seem to be less than that which would reM sult from not making it (see paragraph 127). Propaganda for example is used for many good purposes, such as discouraging child abuse or race hatred. [14] Sex education is obviously useful, yet the effect of sex education (to the extent that it is successful) is to take the shaping of sexual attitudes away from the family and put it into the hands of the state as represented by the public school 154. Suppose a biological trait is discovered that increases the likelihood tM a child will grow up to be a criminal, and suppose some sort of gene therapy can remove this trait. [29] Of course most parents whose children possess the trait will have them undergo the therapy. It would be inhumane to do otherwise, since the child would probably have a miserable life if he grew up to be a criminal. But many or most primitive societies have a low crime rate in comparison with that of our society, even though they have neither high- tech methods of chiM ld-rearing nor harsh systems of punishment. Since there is no reason to suppose that more modern men than primitive men have innate predatory tendencies, the high crime rate of our society must be due to the pressures that modern conditions put on people, to which many cannot or will not adjust. Thus a treatment designed to remove potential criminal tendencies is at least in part a way of re-engineering people so that they suit the requirements of the system. 155. Our society teM any mode of thought or behavior that is inconvenient for the system, and this is plausible because when an individual doesn t fit into the system it causes pain to the individual as well as problems for the system. Thus the manipulation of an individual to adjust him to the system is seen as a 156. In paragraph 127 we pointed out that if the use of a new item of technology is INITIALLY optM ional, it does not necessarily REMAIN optional, because the new technology tends to change society in such a way that it becomes difficult or impossible for an individual to function without using that technology. This applies also to the technology of human behavior. In a world in which most children are put through a program to make them enthusiastic about studying, a parent will almost be forced to put his kid through such a program, because if he does not, then the kid will gM to be, comparatively speaking, an ignoramus and therefore unemployable. Or suppose a biological treatment is discovered that, without undesirable side-effects, will greatly reduce the psychological stress from which so many people suffer in our society. If large numbers of people choose to undergo the treatment, then the general level of stress in society will be reduced, so that it will be possible for the system to increase the stress-producing pressures. In fact, M something like this seems to have happened already with one of our society s most important psychological tools for enabling people to reduce (or at least temporarily escape from) stress, namely, mass entertainment (see paragraph 147). Our use of mass entertainment is : No law requires us to watch television, listen to the radio, read magazines. Yet mass entertainment is a means of escape and stress-reduction on which most of us have become dependent. Everyone complains about the trashiness of television, but almost everyone watches it. A few have kicked the TV habit, but it would be a rare person who could get along today without using ANY form of mass entertainment. (Yet until quite recently in human history most people got along very nicely with no other entertainment than that which each local community created for itself.) Without the entertainment industry the system probably would not have been able to get away with putting aM s much stress-producing pressure on us as it does. 157. Assuming that industrial society survives, it is likely that technology will eventually acquire something approaching complete control over human behavior. It has been established beyond any rational doubt that human thought and behavior have a largely biological basis. As experimenters have demonstrated, feelings such as hunger, pleasure, anger and fear can be turned on and off by electrical stimulation of appropriate partM brain. Memories can be destroyed by damaging parts of the brain or they can be brought to the surface by electrical stimulation. Hallucinations can be induced or moods changed by drugs. There may or may not be an immaterial human soul, but if there is one it clearly is less powerful that the biological mechanisms of human behavior. For if that were not the case then researchers would not be able so easily to manipulate human feelings and behavior with drugs and eleM 158. It presumably would be impractical for all people to have electrodes inserted in their heads so that they could be controlled by the authorities. But the fact that human thoughts and feelings are so open to biological intervention shows that the problem of controlling human behavior is mainly a technical problem; a problem of neurons, hormones and complex molecules; the kind of problem that is accessible to scientific attack. Given the outstanding record oM f our society in solving technical problems, it is overwhelmingly probable that great advances will be made in the control of human behavior. 159. Will public resistance prevent the introduction of technological control of human behavior? It certainly would if an attempt were made to introduce such control all at once. But since technological control will be introduced through a long sequence of small advances, there will be no rational and effective public resistance. (See paraM graphs 127, 132, 153.) 160. To those who think that all this sounds like science fiction, we point out s science fiction is today s fact. The Industrial Revolution has radically altered man s environment and way of life, and it is only to be expected that as technology is increasingly applied to the human body and mind, man himself will be altered as radically as his environment and way of life have been. HUMAN RACE AT A CROSSROADS 161. But we have gotten ahead M of our story. It is one thing to develop in the laboratory a series of psychological or biological techniques for manipulating human behavior and quite another to integrate these techniques into a functioning social system. The latter problem is the more difficult of the two. For example, while the techniques of educational psychology doubtless work quite well in the where they are developed, it is not necessarily easy to apply them effectively throughout our educational system. We all know what many of our schools are like. The teachers are too busy taking knives and guns away from the kids to subject them to the latest techniques for making them into computer nerds. Thus, in spite of all its technical advances relating to human behavior, the system to date has not been impressively successful in controlling human beings. The people whose behavior is fairly well under the control of the system are those of the type that might be M growing numbers of people who in one way or another are rebels against the system: welfare leaches, youth gangs, cultists, satanists, nazis, radical environmentalists, militiamen, etc. 162. The system is currently engaged in a desperate struggle to overcome certain problems that threaten its survival, among which the problems of human behavior are the most important. If the system succeeds in acquiring sufficient control over human behavior M quickly enough, it will probably survive. Otherwise it will break down. We think the issue will most likely be resolved within the next several decades, say 40 to 100 years. 163. Suppose the system survives the crisis of the next several decades. By that time it will have to have solved, or at least brought under control, the principal problems that confront it, in particular that of human beings; that is, making people sufficiently docile so that heir no longer threatens the system. That being accomplished, it does not appear that there would be any further obstacle to the development of technology, and it would presumably advance toward its logical conclusion, which is complete control over everything on Earth, including human beings and all other important organisms. The system may become a unitary, monolithic organization, or it may be more or less fragmented and consist of a number of organizations coexisting in a relatioM nship that includes elements of both cooperation and competition, just as today the government, the corporations and other large organizations both cooperate and compete with one another. Human freedom mostly will have vanished, because individuals and small groups will be impotent vis-a-vis large organizations armed with supertechnology and an arsenal of advanced psychological and biological tools for manipulating human beings, besides instruments of surveillance and physiM cal coercion. Only a small number of people will have any real power, and even these probably will have only very limited freedom, because their behavior too will be regulated; just as today our politicians and corporation executives can retain their positions of power only as long as their behavior remains within certain fairly narrow limits. t imagine that the systems will stop developing further techniques for controlling human beings and nature once the crisis of the neM is over and increasing control is no longer necessary for the system survival. On the contrary, once the hard times are over the system will increase its control over people and nature more rapidly, because it will no longer be hampered by difficulties of the kind that it is currently experiencing. Survival is not the principal motive for extending control. As we explained in paragraphs 87-90, technicians and scientists carry on their work largely as a suM rrogate activity; that is, they satisfy their need for power by solving technical problems. They will continue to do this with unabated enthusiasm, and among the most interesting and challenging problems for them to solve will be those of understanding the human body and mind and intervening in their development. For the 165. But suppose on the other hand that the stresses of the coming decades prove to be too much for the system. If the systemM breaks down there may be a period of chaos, a such as those that history has recorded at various epochs in the past. It is impossible to predict what would emerge from such a time of troubles, but at any rate the human race would be given a new chance. The greatest danger is that industrial society may begin to reconstitute itself within the first few years after the breakdown. Certainly there will be many people (power-hungry types especially) who wM ill be anxious to get the factories running again. 166. Therefore two tasks confront those who hate the servitude to which the industrial system is reducing the human race. First, we must work to heighten the social stresses within the system so as to increase the likelihood that it will break down or be weakened sufficiently so that a revolution against it becomes possible. Second, it is necessary to develop and propagate an ideology that opposes technology and the ociety if and when the system becomes sufficiently weakened. And such an ideology will help to assure that, if and when industrial society breaks down, its remnants will be smashed beyond repair, so that the system cannot be reconstituted. The factories should be destroyed, technical books burned, etc. 167. The industrial system will not break down purely as a result of revolutionary action. It will not be vulnerable to revolutionary attack unless its own internM al problems of development lead it into very serious difficulties. So if the system breaks down it will do so either spontaneously, or through a process that is in part spontaneous but helped along by revolutionaries. If the breakdown is sudden, many people will die, since the world s population has become so overblown that it cannot even feed itself any longer without advanced technology. Even if the breakdown is gradual enough so that reduction of the population can occur morM through lowering of the birth rate than through elevation of the death rate, the process of de- industrialization probably will be very chaotic and involve much suffering. It is naive to think it likely that technology can be phased out in a smoothly managed, orderly way, especially since the technophiles will fight stubbornly at every step. Is it therefore cruel to work for the breakdown of the system? Maybe, but maybe not. In the first place, revolutionaries will not beM able to break the system down unless it is already in enough trouble so that there would be a good chance of its eventually breaking down by itself anyway; and the bigger the system grows, the more disastrous the consequences of its breakdown will be; so it may be that revolutionaries, by hastening the onset of the breakdown, will be reducing the extent of the disaster. 168. In the second place, one has to balance struggle and death against the loss of freedom and dignity. To mM any of us, freedom and dignity are more important than a long life or avoidance of physical pain. Besides, we all have to die some time, and it may be better to die fighting for survival, or for a cause, than to live a long but empty and purposeless life. 169. In the third place, it is not at all certain that survival of the system will lead to less suffering than breakdown of the system would. The system has already caused, and is continuing to cause, immense suffering all over the world. Ancient cultures, that for hundreds of years gave people a satisfactory relationship with each other and with their environment, have been shattered by contact with industrial society, and the result has been a whole catalogue of economic, environmental, social and psychological problems. One of the effects of the intrusion of industrial society has been that over much of the world traditional controls on population have been thrown out of balance. Hence the populatioM n explosion, with all that that implies. Then there is the psychological suffering that is widespread throughout the supposedly fortunate countries of the West (see paragraphs 44, 45). No one knows what will happen as a result of ozone depletion, the greenhouse effect and other environmental problems that cannot yet be foreseen. And, as nuclear proliferation has shown, new technology cannot be kept out of the hands of dictators and irresponsible Third World nations. ou like to speculate about what Iraq or North Korea will do with genetic engineering? say the technophiles, Science is going to fix all that! We will conquer famine, eliminate psychological suffering, make everybody healthy s what they said 200 years ago. The Industrial Revolution was supposed to eliminate poverty, make everybody happy, etc. The actual result has been quite different. The technophiles are hopelessly lf-deceiving) in their understanding of social problems. They are unaware of (or choose to ignore) the fact that when large changes, even seemingly beneficial ones, are introduced into a society, they lead to a long sequence of other changes, most of which are impossible to predict (paragraph 103). The result is disruption of the society. So it is very probable that in their attempts to end poverty and disease, engineer docile, happy personalities and so forth, the technophiles wM social systems that are terribly troubled, even more so than the present once. For example, the scientists boast that they will end famine by creating new, genetically engineered food plants. But this will allow the human population to keep expanding indefinitely, and it is well known that crowding leads to increased stress and aggression. This is merely one example of the PREDICTABLE problems that will arise. We emphasize that, as past experience has shown, techM nical progress will lead to other new problems that CANNOT be predicted in advance (paragraph 103). In fact, ever since the Industrial Revolution, technology has been creating new problems for society far more rapidly than it has been solving old ones. Thus it will take a long and difficult period of trial and error for the technophiles to work the bugs out of their Brave New World (if they every do). In the meantime there will be great suffering. So it is not at all that the survival of industrial society would involve less suffering than the breakdown of that society would. Technology has gotten the human race into a fix from which there is not likely to be any easy escape. 171. But suppose now that industrial society does survive the next several decades and that the bugs do eventually get worked out of the system, so that it functions smoothly. What kind of system will it be? We will consider several possibilities. us postulate that the computer scientists succeed in developing intelligent machines that can do all things better than human beings can do them. In that case presumably all work will be done by vast, highly organized systems of machines and no human effort will be necessary. Either of two cases might occur. The machines might be permitted to make all of their own decisions without human oversight, or else human control over the machines might be retained. 173. If the machines aM re permitted to make all their own decisions, we can make any conjectures as to the results, because it is impossible to guess how such machines might behave. We only point out that the fate of the human race would be at the mercy of the machines. It might be argued that the human race would never be foolish enough to hand over all power to the machines. But we are suggesting neither that the human race would voluntarily turn power over to the machines nor that the machines wM willfully seize power. What we do suggest is that the human race might easily permit itself to drift into a position of such dependence on the machines that it would have no practical choice but to accept all of the decisions. As society and the problems that face it become more and more complex and as machines become more and more intelligent, people will let machines make more and more of their decisions for them, simply because machine-made decisions wilM l bring better results than man-made ones. Eventually a stage may be reached at which the decisions necessary to keep the system running will be so complex that human beings will be incapable of making them intelligently. At that stage the machines will be in effective control. People won t be able to just turn the machines off, because they will be so dependent on them that turning them off would amount to suicide. 174. On the other hand it is possible that human control overM be retained. In that case the average man may have control over certain private machines of his own, such as his car or his personal computer, but control over large systems of machines will be in the hands of a tiny just as it is today, but with two differences. Due to improved techniques the elite will have greater control over the masses; and because human work will no longer be necessary the masses will be superfluous, a useless burden on the sM ystem. If the elite is ruthless they may simply decide to exterminate the mass of humanity. If they are humane they may use propaganda or other psychological or biological techniques to reduce the birth rate until the mass of humanity becomes extinct, leaving the world to the elite. Or, if the elite consists of soft- hearted liberals, they may decide to play the role of good shepherds to the rest of the human race. They will see to it that everyone s physical needs are satisfieM children are raised under psychologically hygienic conditions, that everyone has a wholesome hobby to keep him busy, and that anyone who may become dissatisfied undergoes life will be so purposeless that people will have to be biologically or psychologically engineered either to remove their need for the power process or to make them their drive for power into some harmless hobby. These engineM ered human beings may be happy in such a society, but they most certainly will not be free. They will have been reduced to the status of domestic animals. 175. But suppose now that the computer scientists do not succeed in developing artificial intelligence, so that human work remains necessary. Even so, machines will take care of more and more of the simpler tasks so that there will be an increasing surplus of human workers at the lower levels of ability. (We see this happeningM already. There are many people who find it difficult or impossible to get work, because for intellectual or psychological reasons they cannot acquire the level of training necessary to make themselves useful in the present system.) On those who are employed, ever-increasing demands will be placed: They will need more and more training, more and more ability, and will have to be ever more reliable, conforming and docile, because they will be more and more like ant organism. Their tasks will be increasingly specialized, so that their work will be, in a sense, out of touch with the real world, being concentrated on one tiny slice of reality. The system will have to use any means that it can, whether psychological or biological, to engineer people to be docile, to have the abilities that the system requires and to their drive for power into some specialized task. But the statement that the people of such a society will havM require qualification. The society may find competitiveness useful, provided that ways are found of directing competitiveness into channels that serve the needs of the system. We can imagine a future society in which there is endless competition for positions of prestige and power. But no more than a very few people will ever reach the top, where the only real power is (see end of paragraph 163). Very repellent is a society in which a person can satisfy hM is need for power only by pushing large numbers of other people out of the way and depriving them of THEIR opportunity for 176. One can envision scenarios that incorporate aspects of more than one of the possibilities that we have just discussed. For instance, it may be that machines will take over most of the work that is of real, practical importance, but that human beings will be kept busy by being given relatively unimportant work. It has been suggested, for example, M a great development of the service industries might provide work for human beings. Thus people would spent their time shining each other driving each other around in taxicabs, making handicrafts for one another, waiting on each other s tables, etc. This seems to us a thoroughly contemptible way for the human race to end up, and we doubt that many people would find fulfilling lives in such pointless busy-work. They would seek other, dangerous outlets (drugsM hate groups) unless they were biologically or psychologically engineered to adapt them to such 177. Needless to say, the scenarios outlined above do not exhaust all the possibilities. They only indicate the kinds of outcomes that seem to us most likely. But we can envision no plausible scenarios that are any more palatable than the ones we ve just described. It is overwhelmingly probable that if the industrial- technological system survivesM the next 40 to 100 years, it will by that time have developed certain general characteristics: Individuals (at least those of the type, who are integrated into the system and make it run, and who therefore have all the power) will be more dependent than ever on large organizations; they will be more than ever and their physical and mental qualities to a significant extent (possibly to a very great extent) will be those that are engineered iM nto them rather than being the results of chance (or of s will, or whatever); and whatever may be left of wild nature will be reduced to remnants preserved for scientific study and kept under the supervision and management of scientists (hence it will no longer be truly wild). In the long run (say a few centuries from now) it is likely that neither the human race nor any other important organisms will exist as we know them today, because once you start modifying organisms tM engineering there is no reason to stop at any particular point, so that the modifications will probably continue until man and other organisms have been utterly transformed. 178. Whatever else may be the case, it is certain that technology is creating for human beings a new physical and social environment radically different from the spectrum of environments to which natural selection has adapted the human race physically and psychologically. If man is not adjusteM this new environment by being artificially re-engineered, then he will be adapted to it through a long and painful process of natural selection. The former is far more likely than the latter. 179. It would be better to dump the whole stinking system and take the 180. The technophiles are taking us all on an utterly reckless ride into the unknown. Many people understand something of what technological progress is doing to us yet take a passive attituM de toward it because they think it is inevitable. But we (FC) don t think it is inevitable. We think it can be stopped, and we will give here some indications of how to go about stopping 181. As we stated in paragraph 166, the two main tasks for the present are to promote social stress and instability in industrial society and to develop and propagate an ideology that opposes technology and the industrial system. When the system becomes sufficiently stressed and unstable, M a revolution against technology may be possible. The pattern would be similar to that of the French and Russian Revolutions. French society and Russian society, for several decades prior to their respective revolutions, showed increasing signs of stress and weakness. Meanwhile, ideologies were being developed that offered a new world view that was quite different from the old one. In the Russian case, revolutionaries were actively working to undermine the old order. Then, wM hen the old system was put under sufficient additional stress (by financial crisis in France, by military defeat in Russia) it was swept away by revolution. What we propose is something along the same lines. 182. It will be objected that the French and Russian Revolutions were failures. But most revolutions have two goals. One is to destroy an old form of society and the other is to set up the new form of society envisioned by the revolutionaries. The French and Russian revolutiM (fortunately!) to create the new kind of society of which they dreamed, but they were quite successful in destroying the old society. We have no illusions about the feasibility of creating a new, ideal form of society. Our goal is only to destroy the existing form of society. 183. But an ideology, in order to gain enthusiastic support, must have a positive ideal as well as a negative one; it must be FOR something as well as AGAINST something. The positive ideal thM at we propose is Nature. That is, WILD nature: those aspects of the functioning of the Earth and its living things that are independent of human management and free of human interference and control. And with wild nature we include human nature, by which we mean those aspects of the functioning of the human individual that are not subject to regulation by organized society but are products of chance, or free will, or God (depending on your religious or philosophical 184. Nature makes a perfect counter-ideal to technology for several reasons. Nature (that which is outside the power of the system) is the opposite of technology (which seeks to expand indefinitely the power of the system). Most people will agree that nature is beautiful; certainly it has tremendous popular appeal. The radical environmentalists ALREADY hold an ideology that exalts nature and opposes technology. [30] It is not necessary for the sake of nature to set up some cM himerical utopia or any new kind of social order. Nature takes care of itself: It was a spontaneous creation that existed long before any human society, and for countless centuries many different kinds of human societies coexisted with nature without doing it an excessive amount of damage. Only with the Industrial Revolution did the effect of human society on nature become really devastating. To relieve the pressure on nature it is not necessary to create a special kind of M social system, it is only necessary to get rid of industrial society. Granted, this will not solve all problems. Industrial society has already done tremendous damage to nature and it will take a very long time for the scars to heal. Besides, even pre-industrial societies can do significant damage to nature. Nevertheless, getting rid of industrial society will accomplish a great deal. It will relieve the worst of the pressure on nature so that the scars can begin to heal. It willM remove the capacity of organized society to keep increasing its control over nature (including human nature). Whatever kind of society may exist after the demise of the industrial system, it is certain that most people will live close to nature, because in the absence of advanced technology there is no other way that people CAN live. To feed themselves they must be peasants or herdsmen or fishermen or hunters, etc. And, generally speaking, local autonomy should tend to incM rease, because lack of advanced technology and rapid communications will limit the capacity of governments or other large organizations to control local communities. 185. As for the negative consequences of eliminating industrial society t eat your cake and have it too. To gain one thing you have to sacrifice another. 186. Most people hate psychological conflict. For this reason they avoid doing any serious thinking about difficult social issues, and they like to haM such issues presented to them in simple, black-and-white terms: THIS is all good and THAT is all bad. The revolutionary ideology should therefore be developed on two levels. 187. On the more sophisticated level the ideology should address itself to people who are intelligent, thoughtful and rational. The object should be to create a core of people who will be opposed to the industrial system on a rational, thought-out basis, with full appreciation of the problems and biguities involved, and of the price that has to be paid for getting rid of the system. It is particularly important to attract people of this type, as they are capable people and will be instrumental in influencing others. These people should be addressed on as rational a level as possible. Facts should never intentionally be distorted and intemperate language should be avoided. This does not mean that no appeal can be made to the emotions, but in making such appeal care should M be taken to avoid misrepresenting the truth or doing anything else that would destroy the intellectual respectability of the ideology. 188. On a second level, the ideology should be propagated in a simplified form that will enable the unthinking majority to see the conflict of technology vs. nature in unambiguous terms. But even on this second level the ideology should not be expressed in language that is so cheap, intemperate or irrational that it alienates people of the thoughM tful and rational type. Cheap, intemperate propaganda sometimes achieves impressive short-term gains, but it will be more advantageous in the long run to keep the loyalty of a small number of intelligently committed people than to arouse the passions of an unthinking, fickle mob who will change their attitude as soon as someone comes along with a better propaganda gimmick. However, propaganda of the rabble-rousing type may be necessary when the system is nearing the point oM f collapse and there is a final struggle between rival ideologies to determine which will become dominant when the old world-view 189. Prior to that final struggle, the revolutionaries should not expect to have a majority of people on their side. History is made by active, determined minorities, not by the majority, which seldom has a clear and consistent idea of what it really wants. Until the time comes for the final push toward revolution [31], the task of revolutM ionaries will be less to win the shallow support of the majority than to build a small core of deeply committed people. As for the majority, it will be enough to make them aware of the existence of the new ideology and remind them of it frequently; though of course it will be desirable to get majority support to the extent that this can be done without weakening the core of seriously committed 190. Any kind of social conflict helps to destabilize the system, but one shouM be careful about what kind of conflict one encourages. The line of conflict should be drawn between the mass of the people and the power-holding elite of industrial society (politicians, scientists, upper-level business executives, government officials, etc.). It should NOT be drawn between the revolutionaries and the mass of the people. For example, it would be bad strategy for the revolutionaries to condemn Americans for their habits of consumption. Instead, the averagM e American should be portrayed as a victim of the advertising and marketing industry, which has suckered him into buying a lot of junk that he doesn t need and that is very poor compensation for his lost freedom. Either approach is consistent with the facts. It is merely a matter of attitude whether you blame the advertising industry for manipulating the public or blame the public for allowing itself to be manipulated. As a matter of strategy one should generally 191. One should think twice before encouraging any other social conflict than that between the power- holding elite (which wields technology) and the general public (over which technology exerts its power). For one thing, other conflicts tend to distract attention from the important conflicts (between power-elite and ordinary people, between technology and nature); for another thing, other conflicts may actually tend to encourage technologization, because each M side in such a conflict wants to use technological power to gain advantages over its adversary. This is clearly seen in rivalries between nations. It also appears in ethnic conflicts within nations. For example, in America many black leaders are anxious to gain power for African Americans by placing back individuals in the technological power-elite. They want there to be many black government officials, scientists, corporation executives and so forth. In this way elping to absorb the African American subculture into the technological system. Generally speaking, one should encourage only those social conflicts that can be fitted into the framework of the conflicts of power-elite vs. ordinary people, technology vs nature. 192. But the way to discourage ethnic conflict is NOT through militant advocacy of minority rights (see paragraphs 21, 29). Instead, the revolutionaries should emphasize that although minorities do suffer more or less advantage, this disadvantage is of peripheral significance. Our real enemy is the industrial- technological system, and in the struggle against the system, ethnic distinctions are of no importance. 193. The kind of revolution we have in mind will not necessarily involve an armed uprising against any government. It may or may not involve physical violence, but it will not be a POLITICAL revolution. Its focus will be on technology and economics, not politics. [32] 194. Probably the revM olutionaries should even AVOID assuming political power, whether by legal or illegal means, until the industrial system is stressed to the danger point and has proved itself to be a failure in the eyes of most people. Suppose for example that some party should win control of the United States Congress in an election. In order to avoid betraying or watering down their own ideology they would have to take vigorous measures to turn economic growth into economic shrinkageM man the results would appear disastrous: There would be massive unemployment, shortages of commodities, etc. Even if the grosser ill effects could be avoided through superhumanly skillful management, still people would have to begin giving up the luxuries to which they have become addicted. Dissatisfaction would grow, the party would be voted out of office and the revolutionaries would have suffered a severe setback. For this reason the revolutiM onaries should not try to acquire political power until the system has gotten itself into such a mess that any hardships will be seen as resulting from the failures of the industrial system itself and not from the policies of the revolutionaries. The revolution against technology will probably have to be a revolution by outsiders, a revolution from below and not from above. 195. The revolution must be international and worldwide. It cannot be carried out on a nation-by-nation baM sis. Whenever it is suggested that the United States, for example, should cut back on technological progress or economic growth, people get hysterical and start screaming that if we fall behind in technology the Japanese will get ahead of us. Holy robots! The world will fly off its orbit if the Japanese ever sell more cars than we do! (Nationalism is a great promoter of technology.) More reasonably, it is argued that if the relatively democratic nations of the world fall behind in technology while nasty, dictatorial nations like China, Vietnam and North Korea continue to progress, eventually the dictators may come to dominate the world. That is why the industrial system should be attacked in all nations simultaneously, to the extent that this may be possible. True, there is no assurance that the industrial system can be destroyed at approximately the same time all over the world, and it is even conceivable that the attempt to overthrow the system cM ould lead instead to the domination of the system by dictators. That is a risk that has to be taken. And it is worth taking, since the difference between a industrial system and one controlled by dictators is small compared with the difference between an industrial system and a non-industrial one. [33] It might even be argued that an industrial system controlled by dictators would be preferable, because dictator-controlled systems usually have cient, hence they are presumably more likely to break down. 196. Revolutionaries might consider favoring measures that tend to bind the world economy into a unified whole. Free trade agreements like NAFTA and GATT are probably harmful to the environment in the short run, but in the long run they may perhaps be advantageous because they foster economic interdependence between nations. It will be easier to destroy the industrial system on a worldwide basis if the worM ld economy is so unified that its breakdown in any one major nation will lead to its breakdown in all industrialized nations. 197. Some people take the line that modern man has too much power, too much control over nature; they argue for a more passive attitude on the part of the human race. At best these people are expressing themselves unclearly, because they fail to distinguish between power for LARGE ORGANIZATIONS and power for INDIVIDUALS and SMALL GROUPS. It is a mistake tM powerlessness and passivity, because people NEED power. Modern man as a collective entity that is, the industrial system has immense power over nature, and we (FC) regard this as evil. But modern INDIVIDUALS and SMALL GROUPS OF INDIVIDUALS have far less power than primitive man ever did. Generally speaking, the vast power of over nature is exercised not by individuals or small groups but by large organizations. To the extent that the averageM modern INDIVIDUAL can wield the power of technology, he is permitted to do so only within narrow limits and only under the supervision and control of the system. (You need a license for everything and with the license come rules and regulations.) The individual has only those technological powers with which the system chooses to provide him. His PERSONAL power over nature is slight. 198. Primitive INDIVIDUALS and SMALL GROUPS actually had considerable power over nature; or maybM e it would be better to say power WITHIN nature. When primitive man needed food he knew how to find and prepare edible roots, how to track game and take it with homemade weapons. He knew how to protect himself from heat, cold, rain, dangerous animals, etc. But primitive man did relatively little damage to nature because the COLLECTIVE power of primitive society was negligible compared to the COLLECTIVE power of industrial society. 199. Instead of arguing for powerlessness and paM ssivity, one should argue that the power of the INDUSTRIAL SYSTEM should be broken, and that this will greatly INCREASE the power and freedom of INDIVIDUALS and SMALL GROUPS. 200. Until the industrial system has been thoroughly wrecked, the destruction of that system must be the revolutionaries ONLY goal. Other goals would distract attention and energy from the main goal. More importantly, if the revolutionaries permit themselves to have any other goal than the destruction ofM technology, they will be tempted to use technology as a tool for reaching that other goal. If they give in to that temptation, they will fall right back into the technological trap, because modern technology is a unified, tightly organized system, so that, in order to retain SOME technology, one finds oneself obliged to retain MOST technology, hence one ends up sacrificing only token amounts of technology. 201. Suppose for example that the revolutionaries took a goal. Human nature being what it is, social justice would not come about spontaneously; it would have to be enforced. In order to enforce it the revolutionaries would have to retain central organization and control. For that they would need rapid long-distance transportation and communication, and therefore all the technology needed to support the transportation and communication systems. To feed and clothe poor people they would have to use agricultural and manufacturinM g technology. And so forth. So that the attempt to insure social justice would force them to retain most parts of the technological system. Not that we have anything against social justice, but it must not be allowed to interfere with the effort to get rid of the technological system. 202. It would be hopeless for revolutionaries to try to attack the system without using SOME modern technology. If nothing else they must use the communications media to spread their message. But tM hey should use modern technology for only ONE purpose: to attack the technological system. 203. Imagine an alcoholic sitting with a barrel of wine in front of him. Suppose he starts saying to himself, t bad for you if used in moderation. Why, they say small amounts of wine are even good for you! It won any harm if I take just one little drink.... Well you know what is going to happen. Never forget that the human race with technology is just like an coholic with a barrel of wine. 204. Revolutionaries should have as many children as they can. There is strong scientific evidence that social attitudes are to a significant extent inherited. No one suggests that a social attitude is a direct outcome of s genetic constitution, but it appears that personality traits are partly inherited and that certain personality traits tend, within the context of our society, to make a person more likely to hold this or that ttitude. Objections to these findings have been raised, but the objections are feeble and seem to be ideologically motivated. In any event, no one denies that children tend on the average to hold social attitudes similar to those of their parents. From our point of view it doesn all that much whether the attitudes are passed on genetically or through childhood training. In either case they ARE passed on. 205. The trouble is that many of the people who are inclined to rebel M the industrial system are also concerned about the population problems, hence they are apt to have few or no children. In this way they may be handing the world over to the sort of people who support or at least accept the industrial system. To insure the strength of the next generation of revolutionaries the present generation should reproduce itself abundantly. In doing so they will be worsening the population problem only slightly. And the important problem is toM get rid of the industrial system, because once the industrial system is gone the world s population necessarily will decrease (see paragraph 167); whereas, if the industrial system survives, it will continue developing new techniques of food production that may enable the world s population to keep increasing almost indefinitely. 206. With regard to revolutionary strategy, the only points on which we absolutely insist are that the single overriding goal must be the ion of modern technology, and that no other goal can be allowed to compete with this one. For the rest, revolutionaries should take an empirical approach. If experience indicates that some of the recommendations made in the foregoing paragraphs are not going to give good results, then those recommendations should be discarded. TWO KINDS OF TECHNOLOGY 207. An argument likely to be raised against our proposed revolution is that it is bound to fail, because (it is claimed) throughout hiM story technology has always progressed, never regressed, hence technological regression is impossible. But this claim is false. 208. We distinguish between two kinds of technology, which we will call small-scale technology and organization-dependent technology. Small-scale technology is technology that can be used by small-scale communities without outside assistance. Organization-dependent technology is technology that depends on large-scale social organization. We are aware ofM significant cases of regression in small-scale technology. But organization-dependent technology DOES regress when the social organization on which it depends breaks down. Example: When the Roman Empire fell apart small-scale technology survived because any clever village craftsman could build, for instance, a water wheel, any skilled smith could make steel by Roman methods, and so forth. But the Romans organization-dependent technology DID regress. TheM ir aqueducts fell into disrepair and were never rebuilt. Their techniques of road construction were lost. The Roman system of urban sanitation was forgotten, so that not until rather recent times did the sanitation of European cities equal that of Ancient Rome. 209. The reason why technology has seemed always to progress is that, until perhaps a century or two before the Industrial Revolution, most technology was small-scale technology. But most of the technology developed sinceM Industrial Revolution is organization-dependent technology. Take the refrigerator for example. Without factory-made parts or the facilities of a post-industrial machine shop it would be virtually impossible for a handful of local craftsmen to build a refrigerator. If by some miracle they did succeed in building one it would be useless to them without a reliable source of electric power. So they would have to dam a stream and build a generator. Generators require large M amounts of copper wire. Imagine trying to make that wire without modern machinery. And where would they get a gas suitable for refrigeration? It would be much easier to build an icehouse or preserve food by drying or picking, as was done before the invention of the refrigerator. 210. So it is clear that if the industrial system were once thoroughly broken down, refrigeration technology would quickly be lost. The same is true of other organization-dependent technology. And once tM his technology had been lost for a generation or so it would take centuries to rebuild it, just as it took centuries to build it the first time around. Surviving technical books would be few and scattered. An industrial society, if built from scratch without outside help, can only be built in a series of stages: You need tools to make tools to make tools to make tools ... . A long process of economic development and progress in social organization is required. the absence of an ideology opposed to technology, there is no reason to believe that anyone would be interested in rebuilding industrial society. The enthusiasm for is a phenomenon peculiar to the modern form of society, and it seems not to have existed prior to the 17th century or thereabouts. 211. In the late Middle Ages there were four main civilizations that were about : Europe, the Islamic world, India, and the Far East (China, Japan, KoM rea). Three of those civilizations remained more or less stable, and only Europe became dynamic. No one knows why Europe became dynamic at that time; historians have their theories but these are only speculation. At any rate, it is clear that rapid development toward a technological form of society occurs only under special conditions. So there is no reason to assume that a long-lasting technological regression cannot be brought about. 212. Would society EVENTUALLY develop againM toward an industrial-technological form? Maybe, but there is no use in worrying about it, since we can predict or control events 500 or 1,000 years in the future. Those problems must be dealt with by the people who will live at that time. THE DANGER OF LEFTISM 213. Because of their need for rebellion and for membership in a movement, leftists or persons of similar psychological type often are unattracted to a rebellious or activist movement whose goals and membership are not initially leftist. The resulting influx of leftish types can easily turn a non-leftist movement into a leftist one, so that leftist goals replace or distort the original goals of the movement. 214. To avoid this, a movement that exalts nature and opposes technology must take a resolutely anti-leftist stance and must avoid all collaboration with leftists. Leftism is in the long run inconsistent with wild nature, with human freedom and with the elimination of modern technology. LeftiM collectivist; it seeks to bind together the entire world (both nature and the human race) into a unified whole. But this implies management of nature and of human life by organized society, and it requires advanced technology. You can t have a united world without rapid transportation and communication, you can t make all people love one another without sophisticated psychological techniques, you can without the necessary technologM ical base. Above all, leftism is driven by the need for power, and the leftist seeks power on a collective basis, through identification with a mass movement or an organization. Leftism is unlikely ever to give up technology, because technology is too valuable a source of collective power. 215. The anarchist [34] too seeks power, but he seeks it on an individual or small-group basis; he wants individuals and small groups to be able to control the circumstances of their own livesM . He opposes technology because it makes small groups dependent on large organizations. 216. Some leftists may seem to oppose technology, but they will oppose it only so long as they are outsiders and the technological system is controlled by non-leftists. If leftism ever becomes dominant in society, so that the technological system becomes a tool in the hands of leftists, they will enthusiastically use it and promote its growth. In doing this they will be repeating a pattern thM at leftism has shown again and again in the past. When the Bolsheviks in Russia were outsiders, they vigorously opposed censorship and the secret police, they advocated self-determination for ethnic minorities, and so forth; but as soon as they came into power themselves, they imposed a tighter censorship and created a more ruthless secret police than any that had existed under the tsars, and they oppressed ethnic minorities at least as much as the tsars had done. In the United States, a couple of decades ago when leftists were a minority in our universities, leftist professors were vigorous proponents of academic freedom, but today, in those of our universities where leftists have become dominant, they have shown themselves ready to take away from everyone s academic freedom. (This is political correctness. happen with leftists and technology: They will use it to oppress everyone else if they ever get it under their ownM 217. In earlier revolutions, leftists of the most power-hungry type, repeatedly, have first cooperated with non-leftist revolutionaries, as well as with leftists of a more libertarian inclination, and later have double- crossed them to seize power for themselves. Robespierre did this in the French Revolution, the Bolsheviks did it in the Russian Revolution, the communists did it in Spain in 1938 and Castro and his followers did it in Cuba. Given the past history of lefM tism, it would be utterly foolish for non-leftist revolutionaries today to collaborate with leftists. 218. Various thinkers have pointed out that leftism is a kind of religion. Leftism is not a religion in the strict sense because leftist doctrine does not postulate the existence of any supernatural being. But, for the leftist, leftism plays a psychological role much like that which religion plays for some people. The leftist NEEDS to believe in leftism; it plays a vital role inM his psychological economy. His beliefs are not easily modified by logic or facts. He has a deep conviction that leftism is morally Right with a capital R, and that he has not only a right but a duty to impose leftist morality on everyone. (However, many of the people we are referring to as do not think of themselves as leftists and would not describe their system of beliefs as leftism. We use the term t know of any better words to deM signate the spectrum of related creeds that includes the feminist, gay rights, political correctness, etc., movements, and because these movements have a strong affinity with the old left. See paragraphs 227-230.) 219. Leftism is a totalitarian force. Wherever leftism is in a position of power it tends to invade every private corner and force every thought into a leftist mold. In part this is because of the quasi-religious character of leftism; everything contrary to leftist belM iefs represents Sin. More importantly, leftism is a totalitarian force because of the leftists for power. The leftist seeks to satisfy his need for power through identification with a social movement and he tries to go through the power process by helping to pursue and attain the goals of the movement (see paragraph 83). But no matter how far the movement has gone in attaining its goals the leftist is never satisfied, because his activism is a surrogate ee paragraph 41). That is, the leftist s real motive is not to attain the ostensible goals of leftism; in reality he is motivated by the sense of power he gets from struggling for and then reaching a social goal. [35] Consequently the leftist is never satisfied with the goals he has already attained; his need for the power process leads him always to pursue some new goal. The leftist wants equal opportunities for minorities. When that is attained he insists on statistical equalM ity of achievement by minorities. And as long as anyone harbors in some corner of his mind a negative attitude toward some minority, the leftist has to re-educated him. And ethnic minorities are not enough; no one can be allowed to have a negative attitude toward homosexuals, disabled people, fat people, old people, ugly people, and on and on and on. It s not enough that the public should be informed about the hazards of smoking; a warning has to be stamped on every packaM ge of cigarettes. Then cigarette advertising has to be restricted if not banned. The activists will never be satisfied until tobacco is outlawed, and after that it will be alcohol, then junk food, etc. Activists have fought gross child abuse, which is reasonable. But now they want to stop all spanking. When they have done that they will want to ban something else they consider unwholesome, then another thing and then another. They will never be satisfied until they have complete M all child rearing practices. And then they will move on to another cause. 220. Suppose you asked leftists to make a list of ALL the things that were wrong with society, and then suppose you instituted EVERY social change that they demanded. It is safe to say that within a couple of years the majority of leftists would find something new to complain about, some new social to correct because, once again, the leftist is motivated less by distress s ills than by the need to satisfy his drive for power by imposing his solutions on society. 221. Because of the restrictions placed on their thoughts and behavior by their high level of socialization, many leftists of the over-socialized type cannot pursue power in the ways that other people do. For them the drive for power has only one morally acceptable outlet, and that is in the struggle to impose their morality on everyone. 222. Leftists, especially those of the oversocializedM type, are True Believers in the sense of Eric Hoffer Believers are of the same psychological type as leftists. Presumably a true-believing nazi, for instance, is very different psychologically from a true-believing leftist. Because of their capacity for single-minded devotion to a cause, True Believers are a useful, perhaps a necessary, ingredient of any revolutionary movement. This presents a problem with which we must aM t know how to deal. We aren harness the energies of the True Believer to a revolution against technology. At present all we can say is that no True Believer will make a safe recruit to the revolution unless his commitment is exclusively to the destruction of technology. If he is committed also to another ideal, he may want to use technology as a tool for pursuing that other ideal (see paragraphs 220, 221). 223. Some readers may say, out leftism is a lot of crap. I know John and Jane who are leftish types and they don totalitarian tendencies. s quite true that many leftists, possibly even a numerical majority, are decent people who sincerely believe in tolerating values (up to a point) and wouldn t want to use high-handed methods to reach their social goals. Our remarks about leftism are not meant to apply to every individual leftist but to describe the general characterM leftism as a movement. And the general character of a movement is not necessarily determined by the numerical proportions of the various kinds of people involved in the movement. 224. The people who rise to positions of power in leftist movements tend to be leftists of the most power- hungry type, because power-hungry people are those who strive hardest to get into positions of power. Once the power-hungry types have captured control of the movement, there are many tists of a gentler breed who inwardly disapprove of many of the actions of the leaders, but cannot bring themselves to oppose them. They NEED their faith in the movement, and because they cannot give up this faith they go along with the leaders. True, SOME leftists do have the guts to oppose the totalitarian tendencies that emerge, but they generally lose, because the power-hungry types are better organized, are more ruthless and Machiavellian and have taken care to build themselM ves a strong power base. 225. These phenomena appeared clearly in Russia and other countries that were taken over by leftists. Similarly, before the breakdown of communism in the USSR, leftish types in the West would seldom criticize that country. If prodded they would admit that the USSR did many wrong things, but then they would try to find excuses for the communists and begin talking about the faults of the West. They always opposed Western military resistance to gression. Leftish types all over the world vigorously protested the U.S. military action in Vietnam, but when the USSR invaded Afghanistan they did nothing. Not that they approved of the Soviet actions; but because of their leftist faith, they just couldn t bear to put themselves in opposition to communism. Today, in those of our universities where political correctness has become dominant, there are probably many leftish types who privately disapprove of the suppression M freedom, but they go along with it anyway. 226. Thus the fact that many individual leftists are personally mild and fairly tolerant people by no means prevents leftism as a whole form having a totalitarian tendency. 227. Our discussion of leftism has a serious weakness. It is still far from clear what we mean by the word t seem to be much we can do about this. Today leftism is fragmented into a whole spectrum of activist movements. Yet nM ot all activist movements are leftist, and some activist movements (e.g., radical environmentalism) seem to include both personalities of the leftist type and personalities of thoroughly un-leftist types who ought to know better than to collaborate with leftists. Varieties of leftists fade out gradually into varieties of non-leftists and we ourselves would often be hard-pressed to decide whether a given individual is or is not a leftist. To the extent that it is defined all, our conception of leftism is defined by the discussion of it that we have given in this article, and we can only advise the reader to use his own judgment in deciding who is a leftist. 228. But it will be helpful to list some criteria for diagnosing leftism. These criteria cannot be applied in a cut and dried manner. Some individuals may meet some of the criteria without being leftists, some leftists may not meet any of the criteria. Again, you just have to use your judgment. 9. The leftist is oriented toward large-scale collectivism. He emphasizes the duty of the individual to serve society and the duty of society to take care of the individual. He has a negative attitude toward individualism. He often takes a moralistic tone. He tends to be for gun control, for sex education and other psychologically educational methods, for social planning, for affirmative action, for multiculturalism. He tends to identify with victims. He tends tM o be against competition and against violence, but he often finds excuses for those leftists who do commit violence. He is fond of using the common catch- phrases of the left, like responsibility. Maybe the best diagnostic trait of the leftist is his tendency to sympathize with the following movements: femM ethnic rights, disability rights, animal rights, political correctness. Anyone who strongly sympathizes with ALL of these movements is almost certainly a leftist. [36] 230. The more dangerous leftists, that is, those who are most power-hungry, are often characterized by arrogance or by a dogmatic approach to ideology. However, the most dangerous leftists of all may be certain oversocialized types who avoid irritating displays of aggressiveness and refrain fromM advertising their leftism, but work quietly and unobtrusively to promote collectivist values, psychological techniques for socializing children, dependence of the individual on the system, and so forth. These crypto- leftists (as we may call them) approximate certain bourgeois types as far as practical action is concerned, but differ from them in psychology, ideology and motivation. The ordinary bourgeois tries to bring people under control of the system M in order to protect his way of life, or he does so simply because his attitudes are conventional. The crypto-leftist tries to bring people under control of the system because he is a True Believer in a collectivistic ideology. The crypto-leftist is differentiated from the average leftist of the oversocialized type by the fact that his rebellious impulse is weaker and he is more securely socialized. He is differentiated from the ordinary well-socialized bourgeois by the factM that there is some deep lack within him that makes it necessary for him to devote himself to a cause and immerse himself in a collectivity. And maybe his (well-sublimated) drive for power is stronger than that of the average bourgeois. 231. Throughout this article we ve made imprecise statements and statements that ought to have had all sorts of qualifications and reservations attached to them; and some of our statements may be flatly false. Lack of sufficient ormation and the need for brevity made it impossible for us to formulate our assertions more precisely or add all the necessary qualifications. And of course in a discussion of this kind one must rely heavily on intuitive judgment, and that can sometimes be wrong. So we don article expresses more than a crude approximation to the truth. 232. All the same, we are reasonably confident that the general outlines of the picture we have painted here are roughly correct. M Just one possible weak point needs to be mentioned. We have portrayed leftism in its modern form as a phenomenon peculiar to our time and as a symptom of the disruption of the power process. But we might possibly be wrong about this. Oversocialized types who try to satisfy their drive for power by imposing their morality on everyone have certainly been around for a long time. But we THINK that the decisive role played by feelings of inferiority, low self-esteem, powerlessneM ss, identification with victims by people who are not themselves victims, is a peculiarity of modern leftism. Identification with victims by people not themselves victims can be seen to some extent in 19th century leftism and early Christianity but as far as we can make out, symptoms of low self-esteem, etc., were not nearly so evident in these movements, or in any other movements, as they are in modern leftism. But we are not in a position to assert confidently that no such moveM existed prior to modern leftism. This is a significant question to which historians ought to give their attention. 1. (Paragraph 19) We are asserting that ALL, or even most, bullies and ruthless competitors suffer from feelings of inferiority. 2. (Paragraph 25) During the Victorian period many oversocialized people suffered from serious psychological problems as a result of repressing or trying to repress their sexual feelings. Freud apparently based his theories eople of this type. Today the focus of socialization has shifted from sex 3. (Paragraph 27) Not necessarily including specialists in engineering or the 4. (Paragraph 28) There are many individuals of the middle and upper classes who resist some of these values, but usually their resistance is more or less covert. Such resistance appears in the mass media only to a very limited extent. The main thrust of propaganda in our society is in favor of the The main reason why these values have become, so to speak, the official values of our society is that they are useful to the industrial system. Violence is discouraged because it disrupts the functioning of the system. Racism is discouraged because ethnic conflicts also disrupt the system, and discrimination wastes the talents of minority-group members who could be useful to the system. Poverty must be because the underclass causes problems for the system and contacM t with the underclass lowers the morale of the other classes. Women are encouraged to have careers because their talents are useful to the system and, more importantly, because by having regular jobs women become better integrated into the system and tied directly to it rather than to their families. This helps to weaken family solidarity. (The leaders of the system say they want to strengthen the family, but they really mean is that they want the family to serve as an effective tool for cializing children in accord with the needs of the system. We argue in paragraphs 51, 52 that the system cannot afford to let the family or other small-scale social groups be strong or autonomous.) 5. (Paragraph 42) It may be argued that the majority of people don make their own decisions but want leaders to do their thinking for them. There is an element of truth in this. People like to make their own decisions in small matters, but making decisions on difficult, fundamental questiM requires facing up to psychological conflict, and most people hate psychological conflict. Hence they tend to lean on others in making difficult decisions. But it does not follow that they like to have decisions imposed upon them without having any opportunity to influence those decisions. The majority of people are natural followers, not leaders, but they like to have direct personal access to their leaders, they want to be able to influence the leaders and participate to some extenM t in making even the difficult decisions. At least to that degree they need autonomy. 6. (Paragraph 44) Some of the symptoms listed are similar to those shown by To explain how these symptoms arise from deprivation with respect to the Common-sense understanding of human nature tells one that lack of goals whose attainment requires effort leads to boredom and that boredom, long continued, often leads eventually to depression. Failure to attain goals leaM frustration and lowering of self-esteem. Frustration leads to anger, anger to aggression, often in the form of spouse or child abuse. It has been shown that long-continued frustration commonly leads to depression and that depression tends to cause guilt, sleep disorders, eating disorders and bad feelings about oneself. Those who are tending toward depression seek pleasure as an antidote; hence insatiable hedonism and excessive sex, with perversions as a means of getting new kicks. M Boredom too tends to cause excessive pleasure-seeking since, lacking other goals, people often use pleasure as a goal. See accompanying The foregoing is a simplification. Reality is more complex, and of course, deprivation with respect to the power process is not the ONLY cause of the symptoms described. By the way, when we mention depression we do not necessarily mean depression that is severe enough to be treated by a psychiatrist. Often only mild forms of are involved. And when we speak of goals we do not necessarily mean long-term, thought-out goals. For many or most people through much of human history, the goals of a hand-to-mouth existence (merely providing oneself and s family with food from day to day) have been quite sufficient. 7. (Paragraph 52) A partial exception may be made for a few passive, inward-looking groups, such as the Amish, which have little effect on the wider society. Apart from these, some genuine small-scale commuM exist in America today. For instance, youth gangs and regards them as dangerous, and so they are, because the members of these groups are loyal primarily to one another rather than to the system, hence the system cannot control them. Or take the gypsies. The gypsies commonly get away with theft and fraud because their loyalties are such that they can always get other gypsies to give their innocence. Obviously the system wouldM serious trouble if too many people belonged to such groups. Some of the early-20th century Chinese thinkers who were concerned with modernizing China recognized the necessity breaking down small-scale social groups such as the family: (According to Sun Yat-sen) the Chinese people needed a new surge of patriotism, which would lead to a transfer of loyalty from the family to the state.... (According to Li Huang) traditional attachments, particularly to the family had to be abaM ndoned if nationalism were to develop in Chinese Political Thought in the Twentieth Century, page 125, page 297.) 8. (Paragraph 56) Yes, we know that 19th century America had its problems, and serious ones, but for the sake of brevity we have to express ourselves in simplified terms. 9. (Paragraph 61) We leave aside the We are speaking of the 10. (Paragraph 62) Some social scientists, educators, als and the like are doing their best to push the social drives into group 1 by trying to see to it that everyone has a satisfactory social 11. (Paragraphs 63, 82) Is the drive for endless material acquisition really an artificial creation of the advertising and marketing industry? Certainly there is no innate human drive for material acquisition. There have been many cultures in which people have desired little material wealth beyond what was necessary to satisfy their basic phM ysical needs (Australian aborigines, traditional Mexican peasant culture, some African cultures). On the other hand there have also been many pre-industrial cultures in which material acquisition has played an important role. So we can s acquisition-oriented culture is exclusively a creation of the advertising and marketing industry. But it is clear that the advertising and marketing industry has had an important part in creating that culture. The ations that spend millions on advertising wouldn that kind of money without solid proof that they were getting it back in increased sales. One member of FC met a sales manager a couple of years ago who was frank enough to tell him, Our job is to make people buy things they He then described how an untrained novice could present people with the facts about a product, and make no sales at all, while a trained and experienced professionM al salesman would make lots of sales to the same people. This shows that people are manipulated into buying 12. (Paragraph 64) The problem of purposelessness seems to have become less serious during the last 15 years or so, because people now feel less secure physically and economically than they did earlier, and the need for security provides them with a goal. But purposelessness has been replaced by frustration over the difficulty of attaining securiM ty. We emphasize the problem of purposelessness because the liberals and leftists would wish to solve our social problems by having society guarantee everyone but if that could be done it would only bring back the problem of purposelessness. The real issue is not whether society provides well or poorly for people s security; the trouble is that people are dependent on the system for their security rather than having it in their own hands. This, by the way, is part M of the reason why some people get worked up about the right to bear arms; possession of a gun puts that aspect of their security in their own hands. 13. (Paragraph 66) Conservatives efforts to decrease the amount of government regulation are of little benefit to the average man. For one thing, only a fraction of the regulations can be eliminated because most regulations are necessary. For another thing, most of the deregulation affects business rather than the average individual, sM o that its main effect is to take power from the government and give it to private corporations. What this means for the average man is that government interference in his life is replaced by interference from big corporations, which may be permitted, for example, to dump more chemicals that get into his water supply and give him cancer. The conservatives are just taking the average man for a sucker, exploiting his resentment of Big Government to promote the power of Big Business. (Paragraph 73) When someone approves of the purpose for which propaganda is being used in a given case, he generally calls it it some similar euphemism. But propaganda is propaganda regardless of the purpose for which it is used. 15. (Paragraph 83) We are not expressing approval or disapproval of the Panama invasion. We only use it to illustrate a point. 16. (Paragraph 95) When the American colonies were under British rule there were fewer and less effectiveM legal guarantees of freedom than there were after the American Constitution went into effect, yet there was more personal freedom in pre-industrial America, both before and after the War of Independence, than there was after the Industrial Revolution took hold in this country. We quote from Violence in America: Historical and Comparative edited by Hugh Davis Graham and Ted Robert Gurr, Chapter 12 by Roger Lane, pages 476-478: The progressive heighteningM of standards of propriety, and with it the increasing reliance on official law enforcement (in 19th century America) ... were common to the whole society.... [T]he change in social behavior is so long term and so widespread as to suggest a connection with the most fundamental of contemporary social processes; that of industrial urbanization Massachusetts in 1835 had a population of some 660,940, 81 percent rural, overwhelmingly preindustrial and native born. It considerable personal freedom. Whether teamsters, farmers or artisans, they were all accustomed to setting their own schedules, and the nature of their work made them physically independent of each other.... Individual problems, sins or even crimes, were not generally cause for wider social concern.... the twin movements to the city and to the factory, both just gathering force in 1835, had a progressive effect on personal behavior throughout the 19tM and into the 20th. The factory demanded regularity of behavior, a life governed by obedience to the rhythms of clock and calendar, the demands of foreman and supervisor. In the city or town, the needs of living in closely packed neighborhoods inhibited many actions previously unobjectionable. Both blue- and white-collar employees in larger establishments were mutually dependent on their fellows; as one man s work fit into anther The results of the new organization of life and work were apparent by 1900, when some 76 percent of the 2,805,346 inhabitants of Massachusetts were classified as urbanites. Much violent or irregular behavior which had been tolerable in a casual, independent society was no longer acceptable in the more formalized, cooperative atmosphere of the later period.... The move to the cities had, in short, produced a more tractable, more socialized, more eneration than its predecessors. 17. (Paragraph 117) Apologists for the system are fond of citing cases in which elections have been decided by one or two votes, but such cases are rare. 18. (Paragraph 119) Today, in technologically advanced lands, men live very similar lives in spite of geographical, religious, and political differences. The daily lives of a Christian bank clerk in Chicago, a Buddhist bank clerk in Tokyo, and a Communist bank clerk in Moscow are far more alike thanM the life of any one of them is like that of any single man who lived a thousand years ago. These similarities are the result of a common technology.... L. Sprague de Camp, The Ancient Engineers, Ballantine edition, page 17. The lives of the three bank clerks are not IDENTICAL. Ideology does have SOME effect. But all technological societies, in order to survive, must evolve along APPROXIMATELY the same trajectory. 19. (Paragraph 123) Just think an irresponsible genetic enM gineer might create a lot of terrorists. 20. (Paragraph 124) For a further example of undesirable consequences of medical progress, suppose a reliable cure for cancer is discovered. Even if the treatment is too expensive to be available to any but the elite, it will greatly reduce their incentive to stop the escape of carcinogens into the 21. (Paragraph 128) Since many people may find paradoxical the notion that a large number of good things can add up to a bad thing, weM an analogy. Suppose Mr. A is playing chess with Mr. B. Mr. C, a Grand Master, is looking over Mr. A s shoulder. Mr. A of course wants to win his game, so if Mr. C points out a good move for him to make, he is doing Mr. A a favor. But suppose now that Mr. C tells Mr. A how to make ALL of his moves. In each particular instance he does Mr. A a favor by showing him his best move, but by making ALL of his moves for him he spoils his game, since there is not point in M s playing the game at all if someone else makes The situation of modern man is analogous to that of Mr. A. The system makes an s life easier for him in innumerable ways, but in doing so it deprives him of control over his own fate. 22. (Paragraph 137) Here we are considering only the conflict of values within the mainstream. For the sake of simplicity we leave out of the picture values like the idea that wild nature is more importM human economic welfare. 23. (Paragraph 137) Self-interest is not necessarily MATERIAL self-interest. It can consist in fulfillment of some psychological need, for example, by s own ideology or religion. 24. (Paragraph 139) A qualification: It is in the interest of the system to permit a certain prescribed degree of freedom in some areas. For example, economic freedom (with suitable limitations and restraints) has proved effective in promoting economic growth.M But only planned, circumscribed, limited freedom is in the interest of the system. The individual must always be kept on a leash, even if the leash is sometimes long (see paragraphs 94, 25. (Paragraph 143) We don t mean to suggest that the efficiency or the potential for survival of a society has always been inversely proportional to the amount of pressure or discomfort to which the society subjects people. That certainly is not the case. There is good reason to believe that many primitive societies subjected people to less pressure than European society did, but European society proved far more efficient than any primitive society and always won out in conflicts with such societies because of the advantages conferred by technology. 26. (Paragraph 147) If you think that more effective law enforcement is unequivocally good because it suppresses crime, then remember that crime as defined by the system is not necessarily what YOU would call crime. Today, smoking marijuana is a and, in some places in the U.S., so is possession of an unregistered handgun. Tomorrow, possession of ANY firearm, registered or not, may be made a crime, and the same thing may happen with disapproved methods of child-rearing, such as spanking. In some countries, expression of dissident political opinions is a crime, and there is no certainty that this will never happen in the U.S., since no constitution or political system lasts forever. If a society needs a large, powerful law enforcement establishment, then there is something gravely wrong with that society; it must be subjecting people to severe pressures if so many refuse to follow the rules, or follow them only because forced. Many societies in the past have gotten by with little or no formal law- enforcement. 27. (Paragraph 151) To be sure, past societies have had means of influencing human behavior, but these have been primitive and of low effectiveness d with the technological means that are now being developed. 28. (Paragraph 152) However, some psychologists have publicly expressed opinions indicating their contempt for human freedom. And the mathematician Claude Shannon was quoted in Omni (August 1987) as saying, I visualize a time when we will be to robots what dogs are to humans, and I 29. (Paragraph 154) This is no science fiction! After writing paragraph 154 we came across an article in ScientifM ic American according to which scientists are actively developing techniques for identifying possible future criminals and for treating them by a combination of biological and psychological means. Some scientists advocate compulsory application of the treatment, which may be available in the near future. (See Seeking the Criminal by W. Wayt Gibbs, Scientific American, March 1995.) Maybe you think this is OK because the treatment would be applied to those who might ecome violent criminals. But of course it won t stop there. Next, a treatment will be applied to those who might become drunk drivers (they endanger human life too), then perhaps to peel who spank their children, then to environmentalists who sabotage logging equipment, eventually to anyone whose behavior is inconvenient for the system. 30. (Paragraph 184) A further advantage of nature as a counter-ideal to technology is that, in many people, nature inspires the kind of reverence at is associated with religion, so that nature could perhaps be idealized on a religious basis. It is true that in many societies religion has served as a support and justification for the established order, but it is also true that religion has often provided a basis for rebellion. Thus it may be useful to introduce a religious element into the rebellion against technology, the more so because Western society today has no strong religious foundation. Religion, nowadays either is used M transparent support for narrow, short-sighted selfishness (some conservatives use it this way), or even is cynically exploited to make easy money (by many evangelists), or has degenerated into crude irrationalism (fundamentalist protestant sects, ), or is simply stagnant (Catholicism, main-line Protestantism). The nearest thing to a strong, widespread, dynamic religion that the West has seen in recent times has been the quasi-religion of leftism, but leftisM m today is fragmented and has no clear, unified, inspiring goal. Thus there is a religious vacuum in our society that could perhaps be filled by a religion focused on nature in opposition to technology. But it would be a mistake to try to concoct artificially a religion to fill this role. Such an invented religion would probably be a failure. Take the example. Do its adherents REALLY believe in it or are they just play-acting? If they are just play-actiM ng their religion will be a flop in the end. It is probably best not to try to introduce religion into the conflict of nature vs. technology unless you REALLY believe in that religion yourself and find that it arouses a deep, strong, genuine response in many other people. 31. (Paragraph 189) Assuming that such a final push occurs. Conceivably the industrial system might be eliminated in a somewhat gradual or piecemeal fashion (see paragraphs 4, 167 and Note 4). 32. (Paragraph 193) It M is even conceivable (remotely) that the revolution might consist only of a massive change of attitudes toward technology resulting in a relatively gradual and painless disintegration of the industrial system. But if this happens we ll be very lucky. It s far more probably that the transition to a nontechnological society will be very difficult and full of conflicts and disasters. 33. (Paragraph 195) The economic and technological structure of a society are far more important than M its political structure in determining the way the average man lives (see paragraphs 95, 119 and Notes 16, 18). 34. (Paragraph 215) This statement refers to our particular brand of anarchism. A wide variety of social attitudes have been called be that many who consider themselves anarchists would not accept our statement of paragraph 215. It should be noted, by the way, that there is a nonviolent anarchist movement whose members probably would not accept FC as anarchist and certainly would not approve of FC 35. (Paragraph 219) Many leftists are motivated also by hostility, but the hostility probably results in part from a frustrated need for power. 36. (Paragraph 229) It is important to understand that we mean someone who sympathizes with these MOVEMENTS as they exist today in our society. One who believes that women, homosexuals, etc., should have equal rights is not necessary a leftist. The feminist, gay rights, etc., moveM ments that exist in our society have the particular ideological tone that characterizes leftism, and if one believes, for example, that women should have equal rights it does not necessarily follow that one must sympathize with the feminist movement as it exists today. If copyright problems make it impossible for this long quotation to be printed, then please change Note 16 to read as follows: 16. (Paragraph 95) When the American colonies were under British rule there were effective legal guarantees of freedom than there were after the American Constitution went into effect, yet there was more personal freedom in pre-industrial America, both before and after the War of Independence, than there was after the Industrial Revolution took hold in this country. In Violence in America: Historical and Comparative edited by Hugh Davis Graham and Ted Robert Gurr, Chapter 12 by Roger Lane, it is explained how in pre-industrial America the averaL person had greater independence and autonomy than he does today, and how the process of industrialization necessarily led to the restriction of personal SjLP1ero q puso nonfinancial data en bloque es 1ero q dijo c/Foundry USA Pool #dropgold/ IjGREFUND:A1160A936F21C75928B8CB609A64DB506EEDAC042200A18094A68E7E385D9A2B LjJProcertif:578a55eff2b5a508af6533a09449ee3b0fd200cec5f2688dc8f004490ce3bc87 &"""""""""""""""""""""""""""""""""""""""r text/plain;charset=utf-8 text/html;charset=utf-8 "title": "Ordinal Loops", "description": "Do Not Fiat", "storyline": "First chapter is observing the hostile FIAT environment in which Bitcoin has been born from the ashes of GFC. Such a contemporary landscape remains a living organism - loop. Furiously, Bitcoin ASCII fights the army of five main government currencies - ad infinitum. Conceptualization starts with 'Object 0' being a circular donut of currency interaction. Over time, these objects are deciphered into abstraction [Object 1 tM o 5] and the 'Object 6' has the Bitcoin logo highlighted and colour coded demonstrating adversity against the government issued currency.", "website": "https://www.ordinalloops.xyz/", "twitter": "https://twitter.com/Ordinal_Loops" { "Object": "0" "inscription": "c2fe83b53f4eb0b8ba2b4748884727887f840332ef02f3f79b455fcf3a3d11ebi0", } { "Object": "1" "inscription": "2e785256005b2aee00fd22038d99891e0026d2620e3c4180c9c2d9706fe4b364i0", } { "Object": "2" "inscription": "dd1f515b828eedabd6b0M be147cf611ca08c20f39058feee9b96efaa2eba43d9di0", } { "Object": "3" "inscription": "821eec8cf7672e232aa34998d0a92e638decd5aab9f36c30bccdaedec662a829i0", } { "Object": "4" "inscription": "fe00c5cb1d042621025b1a9bbb36d682607f0efe1182ec5109840d3fccd7ce4di0", } { "Object": "5" "inscription": "9c5c6e9b6769fd5eda0aef192d9e39a6133b28ffb3ea2ada5e40b04266485e5bi0", } { "Object": "6" "inscription": "51db0e3702a0679e8adedeab75d58bc1cabfc5f3d53ff6f59e07de62365dc124i0", } 6j4ion:1.QmP72xL8L516W516ihb8dJxkusim5SqHsqovAXfg5mhMgL Aj?=:ETH.ETH:0xdCb7C510423E8c197FD8503d161a9cc4F462086c:38300:te:0 )j'11hLbkAPQzqhohneXthhouZy16KQXmUMGGqGrRh FjDOUT:D139DEC2EABAA85DD8DC25C7F1BFA35B7657CE445794F6885F8FE83965EAC0A8 FjDOUT:D7C3ED8D9992C353263C9ECD38B3C73B2BC8D8212A456E68A6427C47B293EBC0 CjA=:AVAX.AVAX:0xe66fbc8be19bd6b60a7fdc743a1019435ee0a04f:34722901:t !22222222222222222222222222222222222222222222222222 1\ Powered by Luxor Tech \ IjGREFUND:16FF38E52C23A71AD390541B9339499F5449CFE3FACB034ABAA8AE02329B73D7 Ahttp://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:ICCProfile="M sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T18:08:59-05:00" xmp:MetadataDate="2023-02-02T18:08:59-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T18:08:59-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> .)10.)-,3:J>36F7,-@WAFLNRSR2>ZaZP`JQRO &O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about=M "" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-01T14:55:30+03:00" xmp:ModifyDate="2023-02-01T14:55:30+03:00" dc:format="image/jpeg" xmpMM:M InstanceID="xmp.iid:29c0327d-f970-8344-90bf-be98edc57fc5" xmpMM:DocumentID="adobe:docid:photoshop:d288bca7-b69c-fa45-9c20-27c80addcac8" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stM Evt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:f1d9e2a9-67df-2e4f-a24f-e2f0dee4dcdd" stEvt:when="2023-02-01T14:55:30+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvtM :parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:29c0327d-f970-8344-90bf-be98edc57fc5" stEvt:when="2023-02-01T14:55:30+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:f1d9e2a9-67df-2e4f-a24f-e2f0dee4dcdd" stRef:documentID="adobe:docid:photoshop:7827a9af-d813-f04d-b2d1-dd034885cb76" stRef:originalDocumentID="xmp.did:8653de18-59b9-7M 643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 EzTXtRaw profile type exif iTXtXML:com.adobe.xmp " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2"M <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:aux="http://ns.adobe.com/exif/1.0/aux/" aux:SerialNumber="20f3f16cb9daf2995495936439d4c8e848849c0c4f91f20e9a0939334ccb1225"/> M M M M '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000"> <title>Bitcoin Face</title> <g style="isolation:isolate"><rect id="Background-2" width="1025" height="1025" style="fill:#3d4b8a" /><g id="Body-4"><path d="M504,494.22h0A412.66,412.66,0,0,1,916.61,906.88V1003a0,0,0,0,1,0,0H91.3a0,0,0,0,1,0,0V906.88A412.66,412.66,0,0,1,504,494.22Z" style="fill:#6c1908" /></g><g id="Head-11"><path d="M164.72,490.82C164.72,683.66,317,840,504.85,840S845,683.66,845,490.82" transform="translate(0.28)" style="fill:#f5b659M " /><path d="M186,490.82c0,180.79,142.76,327.34,318.87,327.34S823.71,671.61,823.71,490.82" transform="translate(0.28)" style="fill:#ecdea0" /><path d="M228.5,490.82c0,156.68,123.72,283.7,276.35,283.7s276.34-127,276.34-283.7" transform="translate(0.28)" style="fill:#e18d27" /><polygon points="506.79 774.52 503.45 774.52 503.45 510.93 781.47 510.93 781.47 514.36 506.79 514.36 506.79 774.52" style="fill:#ecdea0" /><polygon points="695.33 688.51 497.93 508.38 760.74 598.31 759.68 601.56 512.31 516.91 697.55 685.94 695.M 33 688.51" style="fill:#ecdea0" /><polygon points="609.89 753.4 503.6 513.35 506.64 511.94 612.93 751.98 609.89 753.4" style="fill:#ecdea0" /><polygon points="506.79 774.52 503.45 774.52 503.45 514.36 228.77 514.36 228.77 510.93 506.79 510.93 506.79 774.52" style="fill:#ecdea0" /><polygon points="314.91 688.51 312.69 685.94 497.93 516.91 250.56 601.56 249.5 598.31 512.31 508.38 314.91 688.51" style="fill:#ecdea0" /><polygon points="400.35 753.4 397.31 751.98 503.6 511.94 506.64 513.35 400.35 753.4" style="fill:#ecdM ea0" /><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M664.28,601.65A12.63,12.63,0,1,1,676.58,589,12.47,12.47,0,0,1,664.28,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,664.28,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M643,579.83a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,643,579.83ZM643,558a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,643,558Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-bM lend-mode:overlay"><path d="M738.68,579.83c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.92,10.56,22.92,23.54S751.32,579.83,738.68,579.83Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.58-9,19.58-20.11S749.48,536.18,738.68,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M685.53,536.18a12.63,12.63,0,1,1,12.3-12.63A12.47,12.47,0,0,1,685.53,536.18Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.08,9.08,0,0,0,685.53,514.36Z" transformM ="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M483.59,732.59c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.93,10.56,22.93,23.54S496.23,732.59,483.59,732.59Zm0-43.65c-10.8,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.58-9,19.58-20.11S494.39,688.94,483.59,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M451.7,688.94A12.63,12.63,0,1,1,464,676.32,12.48,12.48,0,0,1,451.7,688.94M Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,451.7,667.12Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M387.93,601.65A12.63,12.63,0,1,1,400.23,589,12.48,12.48,0,0,1,387.93,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,387.93,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M324.16,558a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,324.16,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2AM 9.09,9.09,0,0,0,324.16,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M260.38,558a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,260.38,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,260.38,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M292.27,645.3c-12.64,0-22.93-10.56-22.93-23.54s10.29-23.54,22.93-23.54,22.93,10.56,22.93,23.54S304.91,645.3,292.27,645.3Zm0-43.65c-10.8M ,0-19.59,9-19.59,20.11s8.79,20.11,19.59,20.11,19.59-9,19.59-20.11S303.07,601.65,292.27,601.65Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M324.16,667.12a12.63,12.63,0,1,1,12.3-12.63A12.48,12.48,0,0,1,324.16,667.12Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,324.16,645.3Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M600.5,667.12a12.63,12.63,0,1,1,12.3-12.63A12.48,12.48,0,0,1,600.M 5,667.12Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.08,9.08,0,0,0,600.5,645.3Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M664.28,710.76a12.63,12.63,0,1,1,12.3-12.62A12.47,12.47,0,0,1,664.28,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,664.28,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M611.13,732.59c-12.64,0-22.92-10.56-22.92-23.54s10.28-23.54,22.92-23.54,22.93,10.56,22.93,23M .54S623.77,732.59,611.13,732.59Zm0-43.65c-10.8,0-19.58,9-19.58,20.11s8.78,20.11,19.58,20.11,19.59-9,19.59-20.11S621.94,688.94,611.13,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M706.79,645.3a12.63,12.63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,706.79,645.3Zm0-21.83a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,706.79,623.47Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M473,579.83a12.63,12.M 63,0,1,1,12.3-12.63A12.49,12.49,0,0,1,473,579.83ZM473,558a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,473,558Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M430.45,536.18a12.63,12.63,0,1,1,12.29-12.63A12.48,12.48,0,0,1,430.45,536.18Zm0-21.82a9.2,9.2,0,1,0,8.95,9.19A9.09,9.09,0,0,0,430.45,514.36Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M579.25,558a12.63,12.63,0,1,1,12.3-12.62A12.48,12.48M ,0,0,1,579.25,558Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,579.25,536.18Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558,601.65A12.63,12.63,0,1,1,570.29,589,12.48,12.48,0,0,1,558,601.65Zm0-21.82a9.2,9.2,0,1,0,9,9.19A9.09,9.09,0,0,0,558,579.83Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M494.22,649.82a17.15,17.15,0,1,1,16.7-17.15A17,17,0,0,1,494.22,649.82Zm0-30.87a13.72,13.72M ,0,1,0,13.36,13.72A13.56,13.56,0,0,0,494.22,619Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M536.73,710.76A12.63,12.63,0,1,1,549,698.14,12.48,12.48,0,0,1,536.73,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,536.73,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M558,758.93a17.15,17.15,0,1,1,16.7-17.15A16.95,16.95,0,0,1,558,758.93Zm0-30.86a13.72,13.72,0,1,0,13.36,13.71A13M .55,13.55,0,0,0,558,728.07Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M366.67,710.76A12.63,12.63,0,1,1,379,698.14,12.48,12.48,0,0,1,366.67,710.76Zm0-21.82a9.2,9.2,0,1,0,9,9.2A9.09,9.09,0,0,0,366.67,688.94Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><rect x="164.72" y="490.82" width="680.24" height="21.82" transform="translate(1009.97 1003.46) rotate(-180)" style="fill:#aea4b3;opacity:0.52;mix-blend-mode:overlay" /><path d="M845.81,50M 7.17C845.81,314.33,693.53,158,505.69,158S165.57,314.33,165.57,507.17" transform="translate(0.28)" style="fill:#f5b659" /><path d="M824.55,507.17c0-180.79-142.76-327.35-318.86-327.35S186.82,326.38,186.82,507.17" transform="translate(0.28)" style="fill:#ecdea0" /><path d="M782,507.17c0-156.69-123.73-283.7-276.35-283.7s-276.35,127-276.35,283.7" transform="translate(0.28)" style="fill:#e18d27" /><polygon points="504.29 223.47 507.63 223.47 507.63 487.06 229.62 487.06 229.62 483.63 504.29 483.63 504.29 223.47" style="fiM ll:#ecdea0" /><polygon points="315.76 309.48 513.15 489.61 250.35 399.68 251.4 396.43 498.77 481.07 313.54 312.04 315.76 309.48" style="fill:#ecdea0" /><polygon points="401.19 244.58 507.49 484.63 504.44 486.05 398.15 246 401.19 244.58" style="fill:#ecdea0" /><polygon points="504.29 223.47 507.63 223.47 507.63 483.63 782.31 483.63 782.31 487.06 504.29 487.06 504.29 223.47" style="fill:#ecdea0" /><polygon points="696.17 309.48 698.39 312.04 513.15 481.07 760.53 396.43 761.58 399.68 498.77 489.61 696.17 309.48" styleM ="fill:#ecdea0" /><polygon points="610.73 244.58 613.77 246 507.49 486.05 504.44 484.63 610.73 244.58" style="fill:#ecdea0" /><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M346.26,396.34A12.63,12.63,0,1,1,334,409,12.48,12.48,0,0,1,346.26,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,346.26,418.16Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M367.51,418.16a12.63,12.63,0,1,1-12.29,12.63A12.48,12.48,0,0,1,367.51,418.16Zm0,21.82a9.2,9.2M ,0,1,0-8.95-9.19A9.08,9.08,0,0,0,367.51,440Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M271.85,418.16c12.65,0,22.93,10.56,22.93,23.54s-10.28,23.54-22.93,23.54-22.92-10.56-22.92-23.54S259.21,418.16,271.85,418.16Zm0,43.65c10.81,0,19.59-9,19.59-20.11s-8.78-20.11-19.59-20.11-19.58,9-19.58,20.11S261.05,461.81,271.85,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M325,461.81a12.63M ,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,325,461.81Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,325,483.63Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M527,265.4c12.64,0,22.92,10.56,22.92,23.54S539.59,312.48,527,312.48,504,301.92,504,288.94,514.3,265.4,527,265.4Zm0,43.65c10.8,0,19.58-9,19.58-20.11s-8.78-20.11-19.58-20.11-19.59,9-19.59,20.11S516.15,309.05,527,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mM ix-blend-mode:overlay"><path d="M558.83,309.05a12.63,12.63,0,1,1-12.3,12.62A12.47,12.47,0,0,1,558.83,309.05Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,558.83,330.87Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M622.61,396.34A12.63,12.63,0,1,1,610.31,409,12.47,12.47,0,0,1,622.61,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,622.61,418.16Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><M path d="M686.38,440a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,686.38,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,686.38,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M750.15,440a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,750.15,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,750.15,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M718.26,352.69c12.65,0,22.93M ,10.56,22.93,23.54s-10.28,23.54-22.93,23.54-22.92-10.56-22.92-23.54S705.62,352.69,718.26,352.69Zm0,43.65c10.81,0,19.59-9,19.59-20.11s-8.78-20.11-19.59-20.11-19.58,9-19.58,20.11S707.46,396.34,718.26,396.34Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M686.38,330.87a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,686.38,330.87Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,686.38,352.69Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="M opacity:0.52;mix-blend-mode:overlay"><path d="M410,330.87a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,410,330.87Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,410,352.69Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M346.26,287.22A12.63,12.63,0,1,1,334,299.85,12.49,12.49,0,0,1,346.26,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,346.26,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlM ay"><path d="M399.4,265.4c12.64,0,22.93,10.56,22.93,23.54S412,312.48,399.4,312.48s-22.93-10.56-22.93-23.54S386.76,265.4,399.4,265.4Zm0,43.65c10.8,0,19.59-9,19.59-20.11s-8.79-20.11-19.59-20.11-19.59,9-19.59,20.11S388.6,309.05,399.4,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M303.74,352.69a12.63,12.63,0,1,1-12.3,12.63A12.48,12.48,0,0,1,303.74,352.69Zm0,21.82a9.2,9.2,0,1,0-9-9.19A9.09,9.09,0,0,0,303.74,374.51Z" transform="translate(0.28)" styM le="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M537.58,418.16a12.63,12.63,0,1,1-12.3,12.63A12.47,12.47,0,0,1,537.58,418.16Zm0,21.82a9.2,9.2,0,1,0-9-9.19A9.08,9.08,0,0,0,537.58,440Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M580.09,461.81a12.63,12.63,0,1,1-12.3,12.62A12.48,12.48,0,0,1,580.09,461.81Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,580.09,483.63Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><gM style="opacity:0.52;mix-blend-mode:overlay"><path d="M431.29,440A12.63,12.63,0,1,1,419,452.61,12.49,12.49,0,0,1,431.29,440Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,431.29,461.81Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M452.54,396.34A12.63,12.63,0,1,1,440.25,409,12.47,12.47,0,0,1,452.54,396.34Zm0,21.82a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,452.54,418.16Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blendM -mode:overlay"><path d="M516.32,348.17a17.15,17.15,0,1,1-16.7,17.15A16.95,16.95,0,0,1,516.32,348.17Zm0,30.86A13.72,13.72,0,1,0,503,365.32,13.55,13.55,0,0,0,516.32,379Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M473.8,287.22a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,473.8,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,473.8,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><patM h d="M452.54,239.06a17.15,17.15,0,1,1-16.7,17.14A17,17,0,0,1,452.54,239.06Zm0,30.86a13.72,13.72,0,1,0-13.36-13.72A13.56,13.56,0,0,0,452.54,269.92Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><g style="opacity:0.52;mix-blend-mode:overlay"><path d="M643.86,287.22a12.63,12.63,0,1,1-12.3,12.63A12.49,12.49,0,0,1,643.86,287.22Zm0,21.83a9.2,9.2,0,1,0-9-9.2A9.09,9.09,0,0,0,643.86,309.05Z" transform="translate(0.28)" style="fill:#aea4b3" /></g><rect x="165.84" y="485.34" width="680.24" height="21.82" style="fillM :#aea4b3;opacity:0.52;mix-blend-mode:overlay" /></g><g id="Face-Accessory-1"><circle cx="516.52" cy="942.46" r="62.02" style="fill:#f96020" /><circle cx="516.52" cy="942.46" r="54.28" style="fill:#d74816" /><rect x="487.05" y="879.79" width="35.52" height="96.79" transform="translate(214.46 1954.46) rotate(-135)" style="fill:#ffa520" /><circle cx="510.95" cy="942.07" r="66.36" style="fill:#f96020" /><circle cx="510.94" cy="942.07" r="58.08" style="fill:#d74816" /><g id="_04CbBX.tif" data-name="04CbBX.tif"><path d="M M499.36,875.6h5.23a5.28,5.28,0,0,0,1.13.08c1.51.13,3,.28,4.52.52a52.15,52.15,0,0,1,10.43,2.72,53.73,53.73,0,0,1,32.17,33.55,52.41,52.41,0,0,1,2.21,10c.15,1.22.26,2.45.31,3.67a1.63,1.63,0,0,0,.08.82v4.31a1.52,1.52,0,0,0-.08.82,46.85,46.85,0,0,1-.52,5.14,52,52,0,0,1-5.39,16.51,53.57,53.57,0,0,1-43.89,28.75,50.62,50.62,0,0,1-8-.07,52.07,52.07,0,0,1-10.21-1.83,53.13,53.13,0,0,1-29.73-21.46,53,53,0,0,1-9.05-25.57,17,17,0,0,0-.16-1.88c0-.61,0-1.22,0-1.83,0-1.1,0-2.2,0-3.3a.77.77,0,0,0,.07-.4,53.09,53.09,0,0,1,5.25-20.37,M 53.67,53.67,0,0,1,35.15-28.6,54.71,54.71,0,0,1,9.34-1.48A8.57,8.57,0,0,0,499.36,875.6Zm-42,53.5a44.61,44.61,0,1,0,44.59-44.62A44.59,44.59,0,0,0,457.33,929.1Z" transform="translate(9.03 13)" style="fill:#ff9317" /><path d="M515.36,927.51a21.52,21.52,0,0,1,5.48,1.69,11.83,11.83,0,0,1,5.27,4.7,12,12,0,0,1,1.52,4.64,17.76,17.76,0,0,1-.27,6.21,10.87,10.87,0,0,1-6.8,8,25.21,25.21,0,0,1-5.77,1.53c-1.34.21-2.68.35-4,.44-.47,0-.47,0-.47.51v7.94c0,.5,0,.51-.51.51H504c-.56,0-.53,0-.53-.53v-7.68c0-.64.05-.57-.58-.57h-5.8c-.46,M 0-.46,0-.47.48v7.73c0,.59,0,.57-.54.57h-5.74c-.55,0-.55,0-.55-.57,0-2.51,0-5,0-7.53,0-.14,0-.27,0-.41s-.06-.29-.26-.27H476.43c-.24,0-.34-.06-.34-.31,0-1.9,0-3.79,0-5.69,0-.27.11-.35.36-.35,1.18,0,2.36,0,3.54,0a10.18,10.18,0,0,0,2.13-.27,1.92,1.92,0,0,0,1.55-1.57,11.89,11.89,0,0,0,.29-2.85V921.52c0-2.47,0-4.95,0-7.43a13.56,13.56,0,0,0-.27-2.69,2,2,0,0,0-1.71-1.68,13.39,13.39,0,0,0-2.75-.24h-2.66c-.47,0-.47,0-.47-.49,0-1.76,0-3.51,0-5.27,0-.6-.06-.56.54-.57h12.55c.57,0,.57,0,.58-.54v-7.89c0-.2.09-.28.28-.26h6c.61,0,.M 56-.06.56.54,0,2.51,0,5,0,7.53,0,.7-.05.62.64.62H503c.46,0,.46,0,.47-.49v-7.94c0-.2.07-.28.27-.26h6c.59,0,.57-.09.57.58v7.84c0,.39,0,.39.39.42a32.44,32.44,0,0,1,6.11,1,17.64,17.64,0,0,1,3.2,1.2,9.17,9.17,0,0,1,5,6.06,14.22,14.22,0,0,1,.12,7.24,10.31,10.31,0,0,1-4.17,6,18.31,18.31,0,0,1-5.34,2.53C515.55,927.36,515.43,927.34,515.36,927.51Zm-18.76,12.4c0,2.77,0,5.53,0,8.3,0,.44,0,.45.44.45h3a44,44,0,0,0,6.39-.31,15,15,0,0,0,3.66-.93,6.31,6.31,0,0,0,3.8-4.16,12,12,0,0,0,.26-6,6.32,6.32,0,0,0-2.8-4.24A9.11,9.11,0,0,0,50M 8.7,932a23.93,23.93,0,0,0-4.61-.65c-2.39-.14-4.78,0-7.17-.08-.26,0-.34.1-.32.33s0,.24,0,.36Zm0-22.76v4.25c0,1.05,0,2.09,0,3.13,0,.25,0,.37.34.37a68.58,68.58,0,0,0,7.72-.23,14.47,14.47,0,0,0,3.67-.89,5.57,5.57,0,0,0,3.45-4A11.75,11.75,0,0,0,512,915a5.49,5.49,0,0,0-3.83-4.58,18.25,18.25,0,0,0-5-.88c-2-.1-4,0-6,0h-.21c-.17,0-.25.08-.24.25s0,.27,0,.41Z" transform="translate(9.03 13)" style="fill:#ff9317" /></g><circle cx="459.8" cy="882.7" r="13.97" style="fill:#ff9317" /><circle cx="438.84" cy="861.74" r="13.97" styleM ="fill:#ff9317" /><circle cx="417.89" cy="840.79" r="13.97" style="fill:#ff9317" /><circle cx="396.93" cy="819.83" r="13.97" style="fill:#ff9317" /><circle cx="564.57" cy="882.7" r="13.97" style="fill:#f96020" /><circle cx="585.53" cy="861.74" r="13.97" style="fill:#f96020" /><circle cx="606.48" cy="840.79" r="13.97" style="fill:#f96020" /><circle cx="627.44" cy="819.83" r="13.97" style="fill:#f96020" /></g><g id="Earrings-1"><circle cx="141.68" cy="475.44" r="61.24" style="fill:#f26227" /><circle cx="141.68" cy="4M 75.44" r="53.6" style="fill:#d74b27" /><g><path d="M134.36,420.84h4.82a6.06,6.06,0,0,0,1,.07c1.4.12,2.79.26,4.18.48A48.47,48.47,0,0,1,154,423.9a49.56,49.56,0,0,1,29.69,31,47.44,47.44,0,0,1,2,9.2c.13,1.13.24,2.25.28,3.39a1.48,1.48,0,0,0,.07.75v4a1.37,1.37,0,0,0-.07.75,42.22,42.22,0,0,1-.48,4.75,47.88,47.88,0,0,1-5,15.23,49.3,49.3,0,0,1-47.87,26.48,47.84,47.84,0,0,1-9.43-1.7,49.07,49.07,0,0,1-27.44-19.8,49.09,49.09,0,0,1-8.35-23.6,12.49,12.49,0,0,0-.15-1.73c0-.57,0-1.13,0-1.69,0-1,0-2,0-3.05a.57.57,0,0,0,.07-.36,47.0M 7,47.07,0,0,1,.76-6.38,48.23,48.23,0,0,1,4.09-12.43,49.32,49.32,0,0,1,41.06-27.75A9.08,9.08,0,0,0,134.36,420.84ZM95.57,470.21A41.17,41.17,0,1,0,136.72,429,41.12,41.12,0,0,0,95.57,470.21Z" transform="translate(4.96 5.26)" style="fill:#f79421" /><path d="M149.13,468.75a19.6,19.6,0,0,1,5.05,1.56,10.88,10.88,0,0,1,4.87,4.33,11.08,11.08,0,0,1,1.4,4.28,16.24,16.24,0,0,1-.25,5.73,10.06,10.06,0,0,1-6.28,7.4,23.28,23.28,0,0,1-5.33,1.41c-1.23.19-2.46.33-3.71.4-.43,0-.44,0-.44.48v7.33c0,.46,0,.46-.47.46h-5.35c-.52,0-.49,0-.49M -.48v-7.09c0-.59.05-.53-.54-.53h-5.34c-.43,0-.43,0-.43.44,0,2.38,0,4.76,0,7.14,0,.55.05.52-.5.52H126c-.5,0-.51,0-.51-.52v-7.33c0-.17-.06-.27-.24-.25s-.26,0-.38,0H113.19c-.21,0-.31,0-.31-.29q0-2.62,0-5.25c0-.25.1-.32.34-.31h3.26a9.33,9.33,0,0,0,2-.26,1.76,1.76,0,0,0,1.43-1.45,11,11,0,0,0,.26-2.63q0-10.31,0-20.61c0-2.29,0-4.57,0-6.86a11.52,11.52,0,0,0-.24-2.48,1.88,1.88,0,0,0-1.58-1.55,12.77,12.77,0,0,0-2.54-.23c-.82,0-1.64,0-2.46,0-.43,0-.43,0-.43-.45v-4.87c0-.56,0-.52.5-.52H125c.52,0,.52,0,.52-.51v-6.9c0-.12,0-.25,M 0-.38s.09-.25.26-.24h5.54c.56,0,.51-.06.51.5,0,2.32,0,4.63,0,6.95,0,.65,0,.58.58.58h5.3c.43,0,.43,0,.43-.46v-7c0-.11,0-.22,0-.33s.07-.26.25-.25h5.54c.55,0,.52-.08.52.54V446c0,.36,0,.35.37.38a30.6,30.6,0,0,1,5.63.91,16.49,16.49,0,0,1,3,1.11,8.5,8.5,0,0,1,4.65,5.6,13.15,13.15,0,0,1,.11,6.68,9.54,9.54,0,0,1-3.85,5.55,17,17,0,0,1-4.93,2.34C149.3,468.61,149.19,468.58,149.13,468.75Zm-17.31,11.44c0,2.55,0,5.1,0,7.66,0,.41,0,.41.41.41H135a40.14,40.14,0,0,0,5.9-.28,14.22,14.22,0,0,0,3.38-.86,5.83,5.83,0,0,0,3.5-3.84,11,11,0M ,0,0,.24-5.51,5.78,5.78,0,0,0-2.58-3.91,8.31,8.31,0,0,0-2.49-1,21.66,21.66,0,0,0-4.25-.6c-2.2-.13-4.41,0-6.62-.07-.23,0-.31.09-.29.3s0,.22,0,.33Q131.81,476.5,131.82,480.19Zm0-21v3.92q0,1.44,0,2.88c0,.23.05.35.31.34a62.82,62.82,0,0,0,7.13-.2,13.67,13.67,0,0,0,3.39-.83,5.13,5.13,0,0,0,3.18-3.67,10.6,10.6,0,0,0,.15-4.45,5,5,0,0,0-3.52-4.22,16.7,16.7,0,0,0-4.64-.82c-1.86-.09-3.72,0-5.58,0H132a.2.2,0,0,0-.22.23v.38Q131.81,456,131.81,459.19Z" transform="translate(4.96 5.26)" style="fill:#f79421" /></g><circle cx="897.68"M cy="478.42" r="61.24" style="fill:#f26227" /><circle cx="897.68" cy="478.42" r="53.6" style="fill:#d74b27" /><g><path d="M890.36,423.81h4.82a5.2,5.2,0,0,0,1,.07c1.4.12,2.79.27,4.18.48a48.5,48.5,0,0,1,9.62,2.52,49.52,49.52,0,0,1,29.69,31,47.6,47.6,0,0,1,2,9.2c.13,1.13.24,2.26.28,3.39a1.52,1.52,0,0,0,.07.76v4a1.4,1.4,0,0,0-.07.76,41.81,41.81,0,0,1-.48,4.74,47.88,47.88,0,0,1-5,15.23,49.44,49.44,0,0,1-40.51,26.54,47.59,47.59,0,0,1-7.36-.06,48.57,48.57,0,0,1-9.43-1.69,49.12,49.12,0,0,1-27.44-19.8,49.17,49.17,0,0,1-8.35M -23.6,12.63,12.63,0,0,0-.15-1.74c0-.56,0-1.12,0-1.69,0-1,0-2,0-3a.62.62,0,0,0,.07-.37,47.11,47.11,0,0,1,.76-6.37,48.1,48.1,0,0,1,4.09-12.43,49.49,49.49,0,0,1,32.44-26.4,50.07,50.07,0,0,1,8.62-1.36A7.27,7.27,0,0,0,890.36,423.81Zm-38.79,49.37A41.17,41.17,0,1,0,892.72,432,41.14,41.14,0,0,0,851.57,473.18Z" transform="translate(4.96 5.26)" style="fill:#f79421" /><path d="M905.13,471.72a19.6,19.6,0,0,1,5,1.56,10.85,10.85,0,0,1,4.87,4.34,11,11,0,0,1,1.4,4.28,16.25,16.25,0,0,1-.25,5.73,10,10,0,0,1-6.28,7.39,23.28,23.28,0,0M ,1-5.33,1.41c-1.23.19-2.46.33-3.71.41-.43,0-.44,0-.44.47v7.33c0,.46,0,.47-.47.47h-5.35c-.52,0-.49,0-.49-.49v-7.09c0-.59,0-.53-.54-.53h-5.34c-.43,0-.43,0-.43.45,0,2.38,0,4.76,0,7.14,0,.54.05.52-.5.52H882c-.5,0-.51,0-.51-.53v-7.33c0-.17-.06-.26-.24-.25H869.19c-.21,0-.31-.05-.31-.28q0-2.62,0-5.25c0-.26.1-.32.34-.32h3.26a9.33,9.33,0,0,0,2-.26,1.76,1.76,0,0,0,1.43-1.45,11,11,0,0,0,.26-2.62q0-10.32,0-20.62c0-2.28,0-4.57,0-6.85a11.55,11.55,0,0,0-.24-2.49,1.88,1.88,0,0,0-1.58-1.55,12.77,12.77,0,0,0-2.54-.23h-2.46c-.43,0-.4M 3,0-.43-.45v-4.87c0-.55,0-.52.5-.52H881c.52,0,.52,0,.52-.5v-6.91c0-.12,0-.25,0-.37s.09-.26.26-.25h5.54c.56,0,.51-.05.51.5,0,2.32,0,4.64,0,6.95,0,.65-.05.58.58.58h5.3c.43,0,.43,0,.43-.45v-7c0-.11,0-.22,0-.33s.07-.26.25-.25h5.54c.55,0,.52-.07.52.55V449c0,.36,0,.36.37.38a30.62,30.62,0,0,1,5.63.92,16.45,16.45,0,0,1,3,1.1,8.5,8.5,0,0,1,4.65,5.6,13.19,13.19,0,0,1,.11,6.69,9.49,9.49,0,0,1-3.85,5.54,16.79,16.79,0,0,1-4.93,2.34C905.3,471.58,905.19,471.56,905.13,471.72Zm-17.31,11.44c0,2.55,0,5.11,0,7.66,0,.41,0,.41.41.41H891M a40.14,40.14,0,0,0,5.9-.29,14.2,14.2,0,0,0,3.38-.85,5.85,5.85,0,0,0,3.5-3.85,11,11,0,0,0,.24-5.51,5.78,5.78,0,0,0-2.58-3.91,8.31,8.31,0,0,0-2.49-1,23.27,23.27,0,0,0-4.25-.6c-2.2-.13-4.41,0-6.62-.07-.23,0-.31.09-.29.31s0,.22,0,.33C887.81,478.25,887.81,480.7,887.82,483.16Zm0-21v3.92c0,1,0,1.92,0,2.89,0,.22.05.34.31.33a62.82,62.82,0,0,0,7.13-.2,13.67,13.67,0,0,0,3.39-.83,5.11,5.11,0,0,0,3.18-3.66,10.65,10.65,0,0,0,.15-4.46,5,5,0,0,0-3.52-4.22,16.68,16.68,0,0,0-4.64-.81c-1.86-.1-3.72,0-5.58-.05H888c-.15,0-.23.07-.22.23M v.38Q887.8,458.92,887.81,462.16Z" transform="translate(4.96 5.26)" style="fill:#f79421" /></g></g><g id="Ears-2"><circle cx="133.72" cy="393.44" r="61.91" style="fill:#883a62" /><circle cx="154.36" cy="469.11" r="41.27" style="fill:#883a62" /><circle cx="181.87" cy="427.84" r="41.27" style="fill:#542f5f" /><g style="opacity:0.5700000000000001;mix-blend-mode:overlay"><line x1="126.08" y1="359.82" x2="194.87" y2="428.6" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="181.87" y1="436M .67" x2="146.52" y2="472.03" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="155.36" y1="392.49" x2="128.85" y2="419" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="164.2" y1="401.32" x2="128.85" y2="436.67" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /></g><circle cx="119.85" cy="534.05" r="53.19" style="fill:#f96020" /><circle cx="119.84" cy="534.05" r="46.55" style="fill:#d74816" /><circle cx="890.1" cy="392.85" r="M 61.91" style="fill:#883a62" /><circle cx="869.47" cy="468.51" r="41.27" style="fill:#883a62" /><circle cx="841.95" cy="427.24" r="41.27" style="fill:#542f5f" /><g style="opacity:0.5700000000000001;mix-blend-mode:overlay"><line x1="897.75" y1="359.22" x2="828.96" y2="428.01" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="841.95" y1="436.08" x2="877.31" y2="471.43" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="868.47" y1="391.89" x2="894.98" y2=M "418.41" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /><line x1="859.63" y1="400.73" x2="894.98" y2="436.08" style="fill:none;stroke:#070707;stroke-miterlimit:10;stroke-width:3px" /></g><circle cx="903.98" cy="533.46" r="53.19" style="fill:#f96020" /><circle cx="903.99" cy="533.46" r="46.55" style="fill:#d74816" /></g><g id="Mouth-5"><polyline points="528.78 551.11 214.79 551.11 336.9 673.22 528.78 673.22 528.78 551.11" style="fill:#4a2955" /><rect x="441.56" y="562.74" width="23.26" heigM ht="29.07" style="fill:#fdf6de" /><rect x="470.63" y="562.74" width="23.26" height="29.07" style="fill:#fdf6de" /><polygon points="406.67 603.44 429.93 562.74 429.93 562.74 406.67 562.74 406.67 603.44" style="fill:#fdf6de" /><polygon points="331.08 664.5 389.23 562.74 389.23 562.74 331.08 562.74 331.08 664.5" style="fill:#fdf6de" /><polygon points="302.84 591.81 319.45 562.74 319.45 562.74 302.84 562.74 302.84 591.81" style="fill:#fdf6de" /><polyline points="480.08 550.62 794.06 550.62 671.96 672.72 480.08 672.72 4M 80.08 550.62" style="fill:#161f26" /><rect x="545.7" y="562.24" width="23.26" height="29.07" transform="translate(1112.99 1153.56) rotate(-180)" style="fill:#fdf6de" /><rect x="516.62" y="562.24" width="23.26" height="29.07" transform="translate(1054.85 1153.56) rotate(-180)" style="fill:#fdf6de" /><rect x="487.55" y="562.24" width="23.26" height="29.07" transform="translate(996.7 1153.56) rotate(-180)" style="fill:#fdf6de" /><polygon points="602.18 602.95 578.92 562.25 578.92 562.25 602.18 562.25 602.18 602.95" stM yle="fill:#fdf6de" /><polygon points="677.77 664 619.63 562.25 619.63 562.25 677.77 562.25 677.77 664" style="fill:#fdf6de" /><polygon points="706.01 591.32 689.4 562.25 689.4 562.25 706.01 562.25 706.01 591.32" style="fill:#fdf6de" /><path d="M443.22,676.13a61.06,61.06,0,1,1,122.11,0" transform="translate(-1.66 0)" style="fill:#e63580" /></g><g id="Eyebrows-1">undefined</g><g id="Glasses-4">undefined</g><g id="Eyes-9"><polyline points="406.74 531.45 329.95 454.66 406.06 454.66" style="fill:#e1d4d1" /><polyline poiM nts="609.46 531.45 686.25 454.66 609.06 454.66" style="fill:#aea4b2" /><path d="M773.72,413a85.24,85.24,0,1,0-170.48,0" transform="translate(0.34)" style="fill:#959acc" /><circle cx="668.36" cy="413.04" r="64.79" style="fill:#727fbd" /><circle cx="667.27" cy="413.04" r="25.85" style="fill:#213f7d" /><polyline points="648.81 394.58 611.88 424.12 648.81 424.12" style="fill:#213f7d" /><circle cx="667.27" cy="413.04" r="18.47" style="fill:#131640" /><path d="M581.29,429.05V368.69A103.31,103.31,0,0,1,684.6,265.38h19.45"M transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M497.89,213.26" transform="translate(0.34)" style="fill:#f1f5fa" /><path d="M241.8,413a85.25,85.25,0,0,1,170.49,0" transform="translate(0.34)" style="fill:#d0bac3" /><circle cx="347.84" cy="413.04" r="64.79" style="fill:#aaaecc" /><polyline points="365.88 394.58 402.81 424.12 365.88 424.12" style="fill:#727fbd" /><circle cx="347.41" cy="413.04" r="25.85" style="fill:M #727fbd" /><circle cx="347.41" cy="413.04" r="18.47" style="fill:#213f7d" /><circle cx="339.93" cy="409.35" r="4.39" style="fill:#eff3f9" /><path d="M431.26,427.83V367.47A103.3,103.3,0,0,0,328,264.16H308.51" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlay" /><path d="M253.74,412.68a78.86,78.86,0,1,1,157.71,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlay" /><patM h d="M268.9,413.43a70.69,70.69,0,1,1,141.37,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlay" /><path d="M296.55,413.15a57.12,57.12,0,0,1,114.24,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.58;mix-blend-mode:overlay" /><path d="M760.73,413.67a78.86,78.86,0,0,0-157.72,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:1M 0;opacity:0.49;mix-blend-mode:overlay" /><path d="M745.56,414.42a70.69,70.69,0,0,0-141.37,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M717.91,414.15a57.12,57.12,0,1,0-114.24,0" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M572.2,428V363.34A110.66,110.66,0,0,1,682.87,252.68h20.84" transform="translate(0.34M )" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M552.15,428.56V354.18A127.29,127.29,0,0,1,679.43,226.9h24" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;opacity:0.49;mix-blend-mode:overlay" /><path d="M440.4,427.44V362.77A110.65,110.65,0,0,0,329.74,252.11H308.9" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlayM " /><path d="M460.46,428V353.61A127.29,127.29,0,0,0,333.18,226.33h-24" transform="translate(0.34)" style="fill:none;stroke:#e9dedb;stroke-linecap:round;stroke-miterlimit:10;mix-blend-mode:overlay" /><circle cx="659.4" cy="409.28" r="4.39" style="fill:#eff3f9" /></g><g id="Nose-1"><path d="M515.59,360.78h0A111.68,111.68,0,0,1,627.27,472.46V598.54a0,0,0,0,1,0,0H403.92a0,0,0,0,1,0,0V472.46A111.68,111.68,0,0,1,515.59,360.78Z" style="fill:#883a62;opacity:0.7000000000000001" /><path d="M458.09,527.5h59.68V427.43H476.51a2M 4.44,24.44,0,0,0-23,16.17l-18.42,51.17A24.45,24.45,0,0,0,458.09,527.5Z" transform="translate(9.03 13)" style="fill:#fa8cb2" /><circle cx="479.97" cy="487.27" r="18.01" style="fill:#542f5f" /><path d="M551.83,527.9H506.16V427.83h27.25a24.45,24.45,0,0,1,23,16.17l18.42,51.17A24.45,24.45,0,0,1,551.83,527.9Z" transform="translate(9.03 13)" style="fill:#f46461" /><circle cx="548.01" cy="486.55" r="18.01" style="fill:#172027" /></g><g id="Hat-3"><rect x="362" y="88" width="144" height="135" style="fill:#542f5f" /><rect x=L "506" y="88" width="144" height="135" style="fill:#172027" /><rect x="254" y="214" width="513" height="18" style="fill:#172027" /></g></g></svg>h! ************************************************** %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz &'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC !22222222222222222222222222222222222222222222222222 filter: 0; jpegRotation: 90; fileterIntensity: 0.000000; filterMask: 0; module:1facing:0; touch: (-1.0, -1.0); AI_Scene: (-1, -1); filter: 0; jpegRotation: 90; fileterInteM nsity: 0.000000; filterMask: 0; module:1facing:0; touch: (-1.0, -1.0); AI_Scene: (-1, -1); !22222222222222222222222222222222222222222222222222 %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz &'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 4.4.0-Exiv2"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:GIMP="http://www.gimp.org/xmp/" xmlns:tiff="http://ns.adobe.com/tiff/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1M .0/" xmpMM:DocumentID="gimp:docid:gimp:8316bccc-2935-4a47-97f1-f21f2f7511e8" xmpMM:InstanceID="xmp.iid:28ad7955-1d0f-4fbc-bcb9-31bd60dd7812" xmpMM:OriginalDocumentID="xmp.did:55268173-c20b-48e2-b1f8-9017bbc1123a" dc:Format="image/webp" GIMP:API="2.0" GIMP:Platform="Windows" GIMP:TimeStamp="1675232775611897" GIMP:Version="2.10.30" tiff:Orientation="1" xmp:CreatorTool="GIMP 2.10"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="saved" stEvt:changed="/" stEvt:instanceID="xmp.iid:82f338de-3b92-418b-9fe3-827fdfb9b654" sM tEvt:softwareAgent="Gimp 2.10 (Windows)" stEvt:when="2023-02-01T07:19:57"/> <rdf:li stEvt:action="saved" stEvt:changed="/" stEvt:instanceID="xmp.iid:ffbb490b-2e1f-4668-8b2d-a092335f6f8d" stEvt:softwareAgent="Gimp 2.10 (Windows)" stEvt:when="2023-02-01T07:26:15"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M MB <?xpacket end="w"?>h! '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC 4j2DC-L5:wyJzxVNyQCr40trKQw5W+FNsq4WPDqI0o0uMj0OLIoY= 9j7+:BTC/BTC::bc1q3f787hr38pmal87yxtpq8tng09q60ljjqqd759:0 IjG=:BNB.TWT-8C2:bnb12w6plevt6kmjdhawzea0u8gjll2nuser5p5e0x:242784547:te:0 c/Foundry USA Pool #dropgold/ FjDOUT:3FD037F22CE424042BB411720B98286FAC6E020A83E9D6013978D2F2C8B72620 IjGREFUND:024EE60586980A9C6377B66335EDB5E4D2E7FD700B153B658005BF01FDFFC9BA CjA=:ETH.ETH:0x09eeC9795f09342b8E364A5ed90B7B58057576b2:2591762:te:0 FjDOUT:9933C1878C9F90EA05ED40599FEF03C94BDD2FBE57720665F193456264205014 FjDOUT:0D13B6AAB253FB9D8D26EC64A3B53817EC43602D04D3A3BB1762FAA251597257 FjDOUT:0E16416FA49D4A73F48A3F1F01E703F8B9940194A6E2250BE15726F4EDD55F3B FjDOUT:3BE8B6600F3DE4BBC31860CB7F50D1EC38B01E0C2DFF2B576D56411345C83D8C FjDOUT:B1ED19234C9A42E00462A84F8BA59E748DC0312EDB91142B3F018BE40D7F988C Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-M c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-M 01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:07:24+03:00" xmp:ModifyDate="2023-02-03T00:07:24+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:10302117-c060-314b-a620-8d00379ec6e5" xmpMM:DocumentID="adobe:docid:photoshop:d9ba050b-ede3-cb49-9a2a-3b3b4140153f" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-M 7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:9d78ffab-bc21-5740-a28e-1a9bdf78970f" stEvt:when="2023-02-03T00:07:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rM df:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:10302117-c060-314b-a620-8d00379ec6e5" stEvt:when="2023-02-03T00:07:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:9d78ffab-bc21-5740-a28e-1aM 9bdf78970f" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:DescriptioM n rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:05:24+03:00" xmp:ModifyDate="2023-02-03T00:05:24+03:00" dc:format="image/M jpeg" xmpMM:InstanceID="xmp.iid:dcbbd076-90e3-714a-ad17-5e3aa8c29009" xmpMM:DocumentID="adobe:docid:photoshop:4b629e50-f7c3-b24e-9a19-ee62c555bbac" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:actioM n="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:0104d6c5-35f3-0e45-a87d-5ffe488fae0f" stEvt:when="2023-02-03T00:05:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="deM rived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:dcbbd076-90e3-714a-ad17-5e3aa8c29009" stEvt:when="2023-02-03T00:05:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:0104d6c5-35f3-0e45-a87d-5ffe488fae0f" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:865M 3de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http:/M /www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:06:11+03:00" xmp:MM odifyDate="2023-02-03T00:06:11+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:87f68be3-dd4b-2349-adc6-93cb6ff19d58" xmpMM:DocumentID="adobe:docid:photoshop:ab05481d-a4ab-ec48-9379-8b5f15ced756" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgentM ="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:8a22ee1f-0bb9-a544-a28a-666c77beb644" stEvt:when="2023-02-03T00:06:11+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adM obe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:87f68be3-dd4b-2349-adc6-93cb6ff19d58" stEvt:when="2023-02-03T00:06:11+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:8a22ee1f-0bb9-a544-a28a-666c77beb644" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-8M 7e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.coM m/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:03:36+03:00" xmp:ModifyDate="2023-02-03T00:03:36+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:ed709d07-309M 4-8241-b761-b5291cf565ed" xmpMM:DocumentID="adobe:docid:photoshop:efe1b116-b36e-144a-9454-8539f97dba60" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496M -9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:e96dedf3-4cfd-9448-8f08-a627edb36972" stEvt:when="2023-02-03T00:03:36+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from applM ication/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:ed709d07-3094-8241-b761-b5291cf565ed" stEvt:when="2023-02-03T00:03:36+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:e96dedf3-4cfd-9448-8f08-a627edb36972" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photosM hop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> yright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB iewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 FjDOUT:13151B37BB35BB9F7A38D91D4216928C060DF7EBD638F840A04405BE5D6D0907 FjDOUT:4080AFC2008608D6852DB07BC067C4FCD027C8FBE4DEDBBA764333FD5DB2976B c/Foundry USA Pool #dropgold/ Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <M rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02M -03T00:03:04+03:00" xmp:ModifyDate="2023-02-03T00:03:04+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:18905f51-745a-4c47-8460-219f1d59abe4" xmpMM:DocumentID="adobe:docid:photoshop:596e5cf2-93e5-3440-b90a-44c991b060c2" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+0M 3:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:9cf81fec-b00c-b941-8732-c91540fb6ec2" stEvt:when="2023-02-03T00:03:04+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parametersM ="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:18905f51-745a-4c47-8460-219f1d59abe4" stEvt:when="2023-02-03T00:03:04+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:9cf81fec-b00c-b941-8732-c91540fb6ec2" stRef:documentID="adobe:docid:photoM shop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RM GB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 DjB=:BNB.BNB:bnb1xehn2r9dhlac5x2q74ur046nknyr2cv9hnckdn:17881693:te:0 Ahttp://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:M ICCProfile="sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T18:27:45-05:00" xmp:MetadataDate="2023-02-02T18:27:45-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T18:27:45-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> .)10.)-,3:J>36F7,-@WAFLNRSR2M &O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO Ahttp://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:M ICCProfile="sRGB IEC61966-2.1" xmp:ModifyDate="2023-02-02T18:24:24-05:00" xmp:MetadataDate="2023-02-02T18:24:24-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T18:24:24-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> .)10.)-,3:J>36F7,-@WAFLNRSR2M &O5-5OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO zTXtRaw profile type exif "title": "The Basics", "description": "Basic HTML and JS Game Collection", "title": "Snake", "inscription": "6edf80efbbae537b554340c31496439b57bef65357a57f21cbb547bc6287d7bfi0" "title": "Tetris", "inscription": "d7f1917503abc4ee718e871acdd3a701cd383c9b8694692d587eebba434fb1c7i0" "title": "Frogger", "inscription": "d3c366b3605b1a212b7e648446eba21215dbdb826fd20a5dc51b7421989c35e0i0" "inscription": "c2b3eec36b0949dee2729265b8aa045d0dd8b24913847fb69e04946b28e68bc2i0" "title": "Sokoban", "inscription": "0eb7edf9d10e27f443d605c3fc986564df3c4914bb33cbaeabb114f5adbd47a9i0" "title": "Puzzle Bobble", "inscription": "64aba1641a97ec70ccd21bb9e99647cae7619900ad173b2d52214754bd9c0a9ei0" "title": "Bomberman", "inscription": "c10adaa4ee52306c8db381da3e136189192fab9c160de3e9f6c56b918afec141i0" "inscription": "6cc328043c8e8837a421e2b869026363010fb10a5e50f89005187cadc429a821i0" "title": "Missile Command", "inscription": "6a3e2377103969dedcdd58fa3d55a31901e6359e8446d97700a6e0a81b71bd24i0" "title": "Doodle Jump", "inscription": "f07e36f8d23758372d076ce17019a784b72046612fd19842d45a1182d382c3d8i0" text/plain;charset=utf-8 /ViaBTC/Mined by nextone2/, KjISWAPTX:0xda0464b36aacb6955fa7c1f9732b93e81d5de4d6548c22f3eabbc3a24d019dab IjGREFUND:F7E0A1C6FD328DC8ACD55F4AA0F6D1036FF7DB0F5E71D6A352FE24B697F43189 EjC=:BNB.ETH-1C9:bnb1krqpk0d4t33uqkrajjfvgx8785tsz76axvgc8l:21290:te:0 Mined by AntPool958[ '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC '#))'#&%,1?5,.;/%&6J7;ACFGF*4MRLDR?EFC C-&-CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC ",+7!!,ZZ`^^b/.<XX] -;=O''8(%%TTYbbe**<<<I/0B328DEU23D423efl@ANXVOTRKEI]?AT1($|. 6&OMFNThRTbWaz*,Bpry c/Foundry USA Pool #dropgold/ Bj@1fb878ce0ca08b1a4b8ba2e88b459710bf5afa2cfae0a88010466a6185159572 EjCs:LTC.LTC:ltc1qq6qse2etz6nk6ld3gmmdggwyydv37504fzt7rg:97473793:ss:0 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmM eta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorToM ol="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:05:13+03:00" xmp:ModifyDate="2023-02-03T00:05:13+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:1c80af2b-cce7-8d42-a1d6-e9008a86e4a4" xmpMM:DocumentID="adobe:docid:photoshop:39b59a96-c41e-f441-94f6-0f310e0bab75" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="crM eated" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:7c566773-5e1c-374f-a2fe-76c79f68ef57" stEvt:when="2023-02-03T00:05:13+03:00" stEvt:softwareAgenM t="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:1c80af2b-cce7-8d42-a1d6-e9008a86e4a4" stEvt:when="2023-02-03T00:05:13+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedM From stRef:instanceID="xmp.iid:7c566773-5e1c-374f-a2fe-76c79f68ef57" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 CjA=:BNB.BNB:bnb1xehn2r9dhlac5x2q74ur046nknyr2cv9hnckdn:3072145:te:0 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:M stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:03:19+03:00" xmp:ModifyDate="2023-02-03T00:03:19+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:cd82dfed-a484-bc4f-a105-fab2d276973b" xmpMM:DocumentID="adobe:docid:photoshop:851dbb5d-b5ce-924e-9f02-86c32bad17ef" xmpMM M:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21M .0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:ef2a2553-7376-db40-881f-d277a7ae9815" stEvt:when="2023-02-03T00:03:19+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:cd82dfedM -a484-bc4f-a105-fab2d276973b" stEvt:when="2023-02-03T00:03:19+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:ef2a2553-7376-db40-881f-d277a7ae9815" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DoM cumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 KjISWAPTX:0xc2db3fe8bfc51988dc335a013c970910d91e65f74f68b7287aa6354afc971918Q FjDOUT:8DB2E32CCAFE708B8A7EC5CF2B11D2A6B851B50CA18864A39B8081EFF69F69B9 FjDOUT:6DA7FBB369E6058B34DA69053488363904660F2FFDDA269D0E1CA0AC62E352E2 FjDOUT:FA5D8703AA6632BD34974F158F4640DE09E3D05332E3ED5DD5A58727EA17A953 LjJ=:ETH.USDC-B48:0x86a680ee2a0C4eed78664931AC9aeFD40E7575a8:195169989017:xdf LjJ=:ETH.USDC-B48:0x86a680ee2a0C4eed78664931AC9aeFD40E7575a8:112509347596:xdf(} text/plain;charset=utf-8 THE TRAGEDY OF ROMEO AND JULIET by William Shakespeare Scene I. A public place. Scene III. Room in Capulet Scene V. A Hall in Capulet Scene I. An open place adjoining Capulet Scene III. Friar Lawrence Scene VI. Friar Lawrence Scene I. A public Place. ene II. A Room in Capulet Scene III. Friar Lawrence Scene IV. A Room in Capulet Scene V. An open Gallery to Juliet s Chamber, overlooking the Garden. Scene I. Friar Lawrence Scene II. Hall in Capulet Scene IV. Hall in Capulet s Chamber; Juliet on the bed. Scene I. Mantua. A Street. Scene II. Friar Lawrence Scene III. A churchyard; in it a Monument belonging tM ESCALUS, Prince of Verona. MERCUTIO, kinsman to the Prince, and friend to Romeo. PARIS, a young Nobleman, kinsman to the Prince. MONTAGUE, head of a Veronese family at feud with the Capulets. LADY MONTAGUE, wife to Montague. ROMEO, son to Montague. BENVOLIO, nephew to Montague, and friend to Romeo. ABRAM, servant to Montague. BALTHASAR, servant to Romeo. CAPULET, head of a Veronese family at feud with the Montagues. LADY CAPULET, wife to CM JULIET, daughter to Capulet. TYBALT, nephew to Lady Capulet. S COUSIN, an old man. PETER, servant to Juliet SAMPSON, servant to Capulet. GREGORY, servant to Capulet. FRIAR LAWRENCE, a Franciscan. FRIAR JOHN, of the same Order. Citizens of Verona; several Men and Women, relations to both houses; Maskers, Guards, Watchmen and Attendants. SCENE. During the greater part of the Play in Verona;M Fifth Act, at Mantua. Two households, both alike in dignity, In fair Verona, where we lay our scene, From ancient grudge break to new mutiny, Where civil blood makes civil hands unclean. From forth the fatal loins of these two foes A pair of star-cross d lovers take their life; d piteous overthrows Doth with their death bury their parents The fearful passage of their death-mark And the continuance of tM Which, but their children s end, nought could remove, Is now the two hours traffic of our stage; The which, if you with patient ears attend, What here shall miss, our toil shall strive to mend. SCENE I. A public place. Enter Sampson and Gregory armed with swords and bucklers. Gregory, on my word, we No, for then we should be colliers. I mean, if we be in choler, we , while you live, draw your neck out o I strike quickly, being moved. But thou art not quickly moved to strike. A dog of the house of Montague moves me. To move is to stir; and to be valiant is to stand: therefore, if thou art moved, thou runn A dog of that house shall move me to stand. I will take the wall of any man or maid of Montague That shows thee a weak slave, for the weakest goes to the wall. True, and therefore women, being the weaker vessels, are ever thrust to the wall: therefore I will push Montague s men from the wall, and thrust his maids to the wall. The quarrel is between our masters and us their men. Tis all one, I will show myself a tyrant: when I have fought with the men I will be civil with the maids, I will cut off their heads. The heads of the maids? Ay, the heads of the maids, or their maidenheads; take it in what sense They must take it in sense that feel it. Me they shall feel while I am able to stand: and pretty piece of flesh. Tis well thou art not fish; if thou hadst, thou hadst been poor John. Draw thy tool; here comes of the house of Montagues. Enter Abram and Balthasar. My naked weapon is out: quarrel, I will back thee. How? Turn thy back and run? No, marry; I fear thee! Let us take the law of our sides; let them begin. I will frown as I pass by, and let them take it as they list. Nay, as they dare. I will bite my thumb at them, which is disgrace to them if they bear it. Do you bite your thumb at us, sir? I do bite my thumb, sir. Do you bite your thumb at us, sir? Is the law of our side if I say ay? No sir, I do not bite my thumb at you, sir; but I bite my thumb, sir. Do you quarrel, sir? Quarrel, sir? No, sir. But if you do, sir, I am for you. I serve as good a man as you. Say better; here comes one of my master Draw, if you be men. Gregory, remember thy washing blow. Part, fools! put up your swords, you know not what you do. [_Beats down their swords._] What, art thou drawn among these heartless hinds? Turn thee Benvolio, look upon thy death. I do but keep the peace, put up thy sword, Or manage it to part these men with me. What, drawn, and talk of peace? I hate the word As I hate hell, all Montagues, and thee: Have at thee, coward. Enter three or four Citizens with clubs. Clubs, bills and partisans! Strike! Beat them down! Down with the Capulets! Down with the MonM Enter Capulet in his gown, and Lady Capulet. What noise is this? Give me my long sword, ho! A crutch, a crutch! Why call you for a sword? My sword, I say! Old Montague is come, And flourishes his blade in spite of me. Enter Montague and his Lady Montague. Thou villain Capulet! Hold me not, let me go. Thou shalt not stir one foot to seek a foe. Enter Prince Escalus, with Attendants. Rebellious subjects, enemM Profaners of this neighbour-stained steel, Will they not hear? What, ho! You men, you beasts, That quench the fire of your pernicious rage With purple fountains issuing from your veins, On pain of torture, from those bloody hands Throw your mistemper d weapons to the ground And hear the sentence of your moved prince. Three civil brawls, bred of an airy word, By thee, old Capulet, and Montague, d the quiet of our streets, by their grave beseeming ornaments, To wield old partisans, in hands as old, d with peace, to part your canker If ever you disturb our streets again, Your lives shall pay the forfeit of the peace. For this time all the rest depart away: You, Capulet, shall go along with me, And Montague, come you this afternoon, To know our farther pleasure in this case, To old Free-town, our common judgement-place. Once more, on pain of death, all men depart. [_Exeunt Prince and Attendants; CapuletM , Lady Capulet, Tybalt, Citizens and Servants._] Who set this ancient quarrel new abroach? Speak, nephew, were you by when it began? Here were the servants of your adversary And yours, close fighting ere I did approach. I drew to part them, in the instant came The fiery Tybalt, with his sword prepar d defiance to my ears, He swung about his head, and cut the winds, Who nothing hurt withal, hiss While we were interchanging thrusts andM Came more and more, and fought on part and part, Till the Prince came, who parted either part. O where is Romeo, saw you him today? Right glad I am he was not at this fray. Madam, an hour before the worshipp d forth the golden window of the east, A troubled mind drave me to walk abroad, Where underneath the grove of sycamore That westward rooteth from this city side, So early walking did I see your son. Towards him I made, but he was ware of me, tole into the covert of the wood. I, measuring his affections by my own, Which then most sought where most might not be found, Being one too many by my weary self, d my humour, not pursuing his, d who gladly fled from me. Many a morning hath he there been seen, With tears augmenting the fresh morning Adding to clouds more clouds with his deep sighs; But all so soon as the all-cheering sun Should in the farthest east begin to draw The shady curtains from AuM Away from light steals home my heavy son, And private in his chamber pens himself, Shuts up his windows, locks fair daylight out And makes himself an artificial night. Black and portentous must this humour prove, Unless good counsel may the cause remove. My noble uncle, do you know the cause? I neither know it nor can learn of him. Both by myself and many other friends; But he, his own affections I will not say how true But to himself so secret and so close, So far from sounding and discovery, As is the bud bit with an envious worm Ere he can spread his sweet leaves to the air, Or dedicate his beauty to the sun. Could we but learn from whence his sorrows grow, We would as willingly give cure as know. See, where he comes. So please you step aside; ll know his grievance or be much denied. I would thou wert so happy by thyM To hear true shrift. Come, madam, let [_Exeunt Montague and Lady Montague._] Good morrow, cousin. Is the day so young? But new struck nine. Ay me, sad hours seem long. Was that my father that went hence so fast? It was. What sadness lengthens Romeo Not having that which, having, makes them short. Out of her favour where I am in love. Alas that love so gentle in his view, Should be so tyrannous and rough in proof. Alas that love, whose view is muffled still, Should, without eyes, see pathways to his will! Where shall we dine? O me! What fray was here? Yet tell me not, for I have heard it all. s much to do with hate, but more with love: Why, then, O brawling love! O loving hate! O anything, of nothing first create! O heavy lightness! serious vanity! Misshapen chaos of well-seeming forms! Feather of lead, briM ght smoke, cold fire, sick health! Still-waking sleep, that is not what it is! This love feel I, that feel no love in this. Dost thou not laugh? No coz, I rather weep. Good heart, at what? Griefs of mine own lie heavy in my breast, Which thou wilt propagate to have it prest With more of thine. This love that thou hast shown Doth add more grief to too much of mine own. Love is a smoke made M with the fume of sighs; d, a fire sparkling in lovers What is it else? A madness most discreet, A choking gall, and a preserving sweet. Soft! I will go along: And if you leave me so, you do me wrong. Tut! I have lost myself; I am not here. This is not Romeo, he Tell me in sadness who is that you love? What, shall I groan and tellM Groan! Why, no; but sadly tell me who. Bid a sick man in sadness make his will, d to one that is so ill. In sadness, cousin, I do love a woman. d so near when I suppos A right good markman, and she A right fair mark, fair coz, is soonest hit. Well, in that hit you miss: she s arrow, she hath Dian And in strong proof of chastity well arM s weak childish bow she lives uncharm She will not stay the siege of loving terms encounter of assailing eyes, Nor ope her lap to saint-seducing gold: s rich in beauty, only poor That when she dies, with beauty dies her store. Then she hath sworn that she will still live chaste? She hath, and in that sparing makes huge waste; d with her severity, Cuts beauty off from all posterity. She is too fair, too wise; wisely toM To merit bliss by making me despair. She hath forsworn to love, and in that vow Do I live dead, that live to tell it now. d by me, forget to think of her. O teach me how I should forget to think. By giving liberty unto thine eyes; Examine other beauties. To call hers, exquisite, in question more. These happy masks that kiss fair ladies Being black, puts us in mind they hide the fair; He that is strucken blind cannot M The precious treasure of his eyesight lost. Show me a mistress that is passing fair, What doth her beauty serve but as a note Where I may read who pass d that passing fair? Farewell, thou canst not teach me to forget. ll pay that doctrine, or else die in debt. Enter Capulet, Paris and Servant. But Montague is bound as well as I, In penalty alike; and tis not hard, I think, For men so old as we to keep the peace. Of honourable reckoning are you both, But now my lord, what say you to my suit? er what I have said before. My child is yet a stranger in the world, She hath not seen the change of fourteen years; Let two more summers wither in their pride Ere we may think her ripe to be a bride. Younger than she are happy mothers made. d are those so early made. The earth hath swallowed all my hopes but she, She is the hopeful lady of my earth: But woo her, gentle Paris, get her heart, My will to her consent is but a part; And she agree, within her scope of choice Lies my consent and fair according voice. This night I hold an old accustom Whereto I have invited many a guest, Such as I love, and you among the store, One more, most welcome, makes my number more. At my poor house look to behold this night Earth-treading stars that make dark heaven light: Such comfort as do lusty young men feel Of limping winter treads, even such delight Among fresh female buds shall you this night Inherit at my house. Hear all, all see, And like her most whose merit most shall be: Which, on more view of many, mine, being one, May stand in number, though in reckoning none. Come, go with me. Go, sirrah, trudge about Through fair Verona; find those persons out Whose names are written there, [_gives a paper_] and to them say, My house and welcome on their pleasure stay. eunt Capulet and Paris._] Find them out whose names are written here! It is written that the shoemaker should meddle with his yard and the tailor with his last, the fisher with his pencil, and the painter with his nets; but I am sent to find those persons whose names are here writ, and can never find what names the writing person hath here writ. I must to the learned. In good Enter Benvolio and Romeo. Tut, man, one fire burns out another Turn giddy, and be holp by backward turning; One desperate grief cures with another Take thou some new infection to thy eye, And the rank poison of the old will die. Your plantain leaf is excellent for that. For what, I pray thee? For your broken shin. Why, Romeo, art thou mad? Not mad, but bound more than a madman is: Shut up in prison, kept without my food, God-den, good fellow. go-den. I pray, sir, can you read? Ay, mine own fortune in my misery. Perhaps you have learned it without book. But I pray, can you read anything you see? Ay, If I know the letters and the language. Ye say honestly, rest you merry! Stay, fellow; I can read. [_He reads the letter._] _Signior Martino and his wife and daughters; County Anselmo and his beauteous sisters; The lady widow of Utruvio; Signior Placentio and his loveM Mercutio and his brother Valentine; Mine uncle Capulet, his wife, and daughters; My fair niece Rosaline and Livia; Signior Valentio and his cousin Tybalt; Lucio and the lively Helena. _ A fair assembly. [_Gives back the paper_] Whither should they come? Indeed I should have ask ll tell you without asking. My master is M the great rich Capulet, and if you be not of the house of Montagues, I pray come and crush a cup of wine. Rest you merry. At this same ancient feast of Capulet Sups the fair Rosaline whom thou so lov With all the admired beauties of Verona. Go thither and with unattainted eye, Compare her face with some that I shall show, And I will make thee think thy swan a crow. When the devout religion of mine eye Maintains such falsehood, then turn tears to fire; ese who, often drown Transparent heretics, be burnt for liars. One fairer than my love? The all-seeing sun er saw her match since first the world begun. Tut, you saw her fair, none else being by, d with herself in either eye: But in that crystal scales let there be weigh s love against some other maid That I will show you shining at this feast, And she shall scant show well that now shows best. ll go along, no such sight M But to rejoice in splendour of my own. SCENE III. Room in Capulet Enter Lady Capulet and Nurse. s my daughter? Call her forth to me. Now, by my maidenhead, at twelve year old, I bade her come. What, lamb! What ladybird! s this girl? What, Juliet! Madam, I am here. What is your will? . Nurse, give leave awhile, We must talk in secret. Nurse, come back again, Thou knowest my daughter Faith, I can tell her age unto an hour. ll lay fourteen of my teeth, And yet, to my teen be it spoken, I have but four, She is not fourteen. How long is it now A fortnight and odd days. Even or odd, of all days in the year, Lammas Eve at night shall she be fourteen. God rest all Christian souls! Were of an age. Well, Susan is with God; She was too good for me. But as I said, On Lammas Eve at night shall she be fourteen; That shall she, marry; I remember it well. Tis since the earthquake now eleven years; I never shall forget it Of all the days of the year, upon that day: For I had then laid wormwood to my dug, Sitting in the sun under the dovehouse wall; My lord and you werM Nay, I do bear a brain. But as I said, When it did taste the wormwood on the nipple Of my dug and felt it bitter, pretty fool, To see it tetchy, and fall out with the dug! Shake, quoth the dovehouse: twas no need, I trow, And since that time it is eleven years; For then she could stand alone; nay, by th She could have run and waddled all about; For even the day before she broke her brow, And then my husband, God be with his soul! dost thou fall upon thy face? Thou wilt fall backward when thou hast more wit; Wilt thou not, Jule? and, by my holidame, The pretty wretch left crying, and said To see now how a jest shall come about. I warrant, and I should live a thousand years, I never should forget it. Wilt thou not, Jule? And, pretty fool, it stinted, and said Enough of this; I pray thee hold thy peace. Yes, madam, yet I cannot choM To think it should leave crying, and say And yet I warrant it had upon it brow A bump as big as a young cockerel A perilous knock, and it cried bitterly. Thou wilt fall backward when thou comest to age; Wilt thou not, Jule? it stinted, and said And stint thou too, I pray thee, Nurse, say I. Peace, I have done. God mark thee to his grace Thou wast the prettiest babe that e And I might live to see thee married once, I have my wish. Marry, that marry is the very theme I came to talk of. Tell me, daughter Juliet, How stands your disposition to be married? It is an honour that I dream not of. An honour! Were not I thine only nurse, I would say thou hadst suck d wisdom from thy teat. Well, think of marriage now: younger than you, Here in Verona, ladies of esteem, Are made already mothers. By my count other much upon these years That you are now a maid. Thus, then, in brief; The valiant Paris seeks you for his love. A man, young lady! Lady, such a man s summer hath not such a flower. s a flower, in faith a very flower. What say you, can you love the gentleman? This night you shall behold him at our feast; er the volume of young Paris And find delight writ there with beaM Examine every married lineament, And see how one another lends content; d in this fair volume lies, Find written in the margent of his eyes. This precious book of love, this unbound lover, To beautify him, only lacks a cover: The fish lives in the sea; and For fair without the fair within to hide. s eyes doth share the glory, That in gold clasps locks in the golden story; So shall you share all that he doth possess, By having him, makiM ng yourself no less. No less, nay bigger. Women grow by men. Speak briefly, can you like of Paris ll look to like, if looking liking move: But no more deep will I endart mine eye Than your consent gives strength to make it fly. Madam, the guests are come, supper served up, you called, my young lady asked for, the Nurse cursed in the pantry, and everything in extremity. I must hence to wait, I beseech you follow straight. Juliet, the County stays. Go, girl, seek happy nights to happy days. Enter Romeo, Mercutio, Benvolio, with five or six Maskers; Torch-bearers and others. What, shall this speech be spoke for our excuse? Or shall we on without apology? The date is out of such prolixity: ll have no Cupid hoodwink s painted bow of lath, like a crow-keeper; Nor no without-book prologue, faintly spoke After the prompter, for our entrance: But let them measure us by what they will, ll measure them a measure, and be gone. Give me a torch, I am not for this ambling; Being but heavy I will bear the light. Nay, gentle Romeo, we must have you dance. Not I, believe me, you have dancing shoes, With nimble soles, I have a soul of lead So stakes me to the ground I cannot move. You are a lover, borM And soar with them above a common bound. I am too sore enpierced with his shaft To soar with his light feathers, and so bound, I cannot bound a pitch above dull woe. s heavy burden do I sink. And, to sink in it, should you burden love; Too great oppression for a tender thing. Is love a tender thing? It is too rough, Too rude, too boisterous; and it pricks like thorn. If love be rough with you, be rough with love; or pricking, and you beat love down. Give me a case to put my visage in: [_Putting on a mask._] A visor for a visor. What care I What curious eye doth quote deformities? Here are the beetle-brows shall blush for me. Come, knock and enter; and no sooner in But every man betake him to his legs. A torch for me: let wantons, light of heart, Tickle the senseless rushes with their heels; d with a grandsire phrase, ll be a candle-holder and look on, er so fair, and I am done. s the mouse, the constable ll draw thee from the mire Or save your reverence love, wherein thou stickest Up to the ears. Come, we burn daylight, ho. I mean sir, in delay We waste our lights in vain, light lights by day. Take our good meaning, for our judgment sits Five times in that ere once in our five wits. And we mean well in going to this mask; I dreamt a dream tonight. Well what was yours? That dreamers often lie. In bed asleep, while they do dream things true. O, then, I see Queen Mab hath been with you. midwife, and she comes In shape no bigger than an agate-stone On the fore-finger of an alderman, Drawn with a team of little atomies s noses as they lie asleep: Her waggon-spokes made M The cover, of the wings of grasshoppers; Her traces, of the smallest spider The collars, of the moonshine s bone; the lash, of film; Her waggoner, a small grey-coated gnat, Not half so big as a round little worm d from the lazy finger of a maid: Her chariot is an empty hazelnut, Made by the joiner squirrel or old grub, And in this state she gallops night by night brains, and then they dream of love; knees, that dream on curtsies straight; fingers, who straight dream on fees; lips, who straight on kisses dream, Which oft the angry Mab with blisters plagues, Because their breaths with sweetmeats tainted are: Sometime she gallops o And then dreams he of smelling out a suit; And sometime comes she with a tithe-pig s nose as a lies asleep, of another benefice: Sometime she driveth o And then dreams he of cutting foreign throats, Of breaches, ambuscados, Spanish blades, Of healths five fathom deep; and then anon Drums in his ear, at which he starts and wakes; And, being thus frighted, swears a prayer or two, And sleeps again. This is that very Mab That plats the manes of horses in the night; And bakes the elf-locks in foul sluttish hairs, Which, once untangled, much misfortune bodes: This is the hag, when maids lieM That presses them, and learns them first to bear, Making them women of good carriage: Peace, peace, Mercutio, peace, True, I talk of dreams, Which are the children of an idle brain, Begot of nothing but vain fantasy, Which is as thin of substance as the air, And more inconstant than the wind, who wooes Even now the frozen bosom of the north, d, puffs away from thence, Turning his side to the dew-droppiM This wind you talk of blows us from ourselves: Supper is done, and we shall come too late. I fear too early: for my mind misgives Some consequence yet hanging in the stars, Shall bitterly begin his fearful date s revels; and expire the term Of a despised life, clos By some vile forfeit of untimely death. But he that hath the steerage of my course Direct my suit. On, lusty gentlemen! Musicians waiting. Enter Servants. s Potpan, that he helps not to take away? He shift a trencher! He scrape a trencher! When good manners shall lie all in one or two men Away with the join-stools, remove the court-cupboard, look to the plate. Good thou, save me a piece of marchpane; and as thou loves me, let the porter let in Susan Grindstone and Nell. AntonM You are looked for and called for, asked for and sought for, in the We cannot be here and there too. Cheerly, boys. Be brisk awhile, and the longer liver take all. Enter Capulet, &c. with the Guests and Gentlewomen to the Maskers. Welcome, gentlemen, ladies that have their toes d with corns will have a bout with you. Ah my mistresses, which of you all to dance? She that makes dainty, ll swear hath corns. Am I come near ye now? Welcome, gentlemen! I have seen the day That I have worn a visor, and could tell A whispering tale in a fair lady Such as would please; You are welcome, gentlemen! Come, musicians, play. A hall, a hall, give room! And foot it, girls. [_Music plays, and they dance._] More light, you knaves; and turn the tables up, And quench the fire, the room is grown too hot. d-for sport comes well. Nay sit, nay sit, good cousin Capulet, For you and I are past our dancing days; t now since last yourself and I r Lady, thirty years. Tis since the nuptial of Lucentio, Come Pentecost as quickly as it will, Some five and twenty years; and then we mask tis more, his son is elder, sir; Will you tell me that? His son was but a ward two years ago. What lady is that, which doth enrich the hand O, she doth teach the torches to burn bright! It seems she hangs upon the cheek of night As a rich jewel in an Ethiop Beauty too rich for use, for earth too dear! So shows a snowy dove trooping with crows er her fellows shows. ll watch her place of stand, g hers, make blessed my rude hand. Did my heart love till now? Forswear it, sight! er saw true beauty till this night. This by his voice, should be a Montague. Fetch me my rapier, boy. What, dares the slave d with an antic face, To fleer and scorn at our solemnity? Now by the stock and honour of my kin, To strike him dead I hold it not a sin. Why how now, kinsman! Wherefore storm you so? Uncle, this is a Montague, our foe; is hither come in spite, To scorn at our solemnity this night. Tis he, that villain Romeo. Content thee, gentle coz, let him alone, A bears him like a portly gentleman; And, to say truth, Verona brags of him To be a virtuous and well-govern I would not for the wealth of all the town Here in my house do him disparagement. Therefore be patient, take no note of him, It is my will; the which if thou respect, Show a fair presence and put ofM An ill-beseeming semblance for a feast. It fits when such a villain is a guest: What, goodman boy! I say he shall, go to; Am I the master here, or you? Go to. ll not endure him! God shall mend my soul, ll make a mutiny among my guests! You will set cock-a-hoop, you You are a saucy boy. Is This trick may chance M to scathe you, I know what. You must contrary me! Marry, Well said, my hearts! You are a princox; go: More light, more light! ll make you quiet. What, cheerly, my hearts. Patience perforce with wilful choler meeting Makes my flesh tremble in their different greeting. I will withdraw: but this intrusion shall, Now seeming sweet, convert to bitter gall. [_To Juliet._] If I profane with my unworthiest hand This holy shrine, theM gentle sin is this, My lips, two blushing pilgrims, ready stand To smooth that rough touch with a tender kiss. Good pilgrim, you do wrong your hand too much, Which mannerly devotion shows in this; For saints have hands that pilgrims And palm to palm is holy palmers Have not saints lips, and holy palmers too? Ay, pilgrim, lips that they must use in prayer. O, then, dear saint, let lips do what hands do: They pray, grant thou, lest faithM Saints do not move, though grant for prayers Then move not while my prayer Thus from my lips, by thine my sin is purg Then have my lips the sin that they have took. Sin from my lips? O trespass sweetly urg Give me my sin again. You kiss by the book. Madam, your mother craves a word with you. Her mother is the ladyM And a good lady, and a wise and virtuous. d her daughter that you talk I tell you, he that can lay hold of her Shall have the chinks. O dear account! My life is my foe Away, be gone; the sport is at the best. Ay, so I fear; the more is my unrest. Nay, gentlemen, prepare not to be gone, We have a trifling foolish banquet towards. en so? Why then, I thank you all; I thank you, honest gentM More torches here! Come on then, let Ah, sirrah, by my fay, it waxes late, [_Exeunt all but Juliet and Nurse._] Come hither, Nurse. What is yond gentleman? The son and heir of old Tiberio. s he that now is going out of door? Marry, that I think be young Petruchio. s he that follows here, that would not dance? Go ask his name. If he be married, is like to be my wedding bed. His name is Romeo, and a Montague, The only son of your great enemy. My only love sprung from my only hate! Too early seen unknown, and known too late! Prodigious birth of love it is to me, That I must love a loathed enemy. [_One calls within, s away, the strangers all are gone. Now old desire doth in his deathbed lie, And young affection gapes to be his heir; That fair for which love groan d for and would die, With tender Juliet match Alike bewitched by the charm of looks; But to his foe suppos s sweet bait from fearful hooks: Being held a foe, he may not have access To breathe such vows as lovers use to swear; And she as much in loM ve, her means much less To meet her new beloved anywhere. But passion lends them power, time means, to meet, Tempering extremities with extreme sweet. SCENE I. An open place adjoining Capulet Can I go forward when my heart is here? Turn back, dull earth, and find thy centre out. [_He climbs the wall and leaps down within it._] Enter Benvolio and Mercutio. Romeo! My cousin Romeo! Romeo! And on my life hath sM He ran this way, and leap d this orchard wall: Call, good Mercutio. Romeo! Humours! Madman! Passion! Lover! Appear thou in the likeness of a sigh, Speak but one rhyme, and I am satisfied; Pronounce but Love and dove; Speak to my gossip Venus one fair word, One nickname for her purblind son and heir, Young Abraham Cupid, he that shot so trim When King Cophetua lov He heareth not, he stiM rreth not, he moveth not; The ape is dead, and I must conjure him. I conjure thee by Rosaline By her high forehead and her scarlet lip, By her fine foot, straight leg, and quivering thigh, And the demesnes that there adjacent lie, That in thy likeness thou appear to us. An if he hear thee, thou wilt anger him. This cannot anger him. To raise a spirit in his mistress Of some strange nature, letting it there stand Till she had laid iM That were some spite. My invocation Is fair and honest, and, in his mistress I conjure only but to raise up him. Come, he hath hid himself among these trees To be consorted with the humorous night. Blind is his love, and best befits the dark. If love be blind, love cannot hit the mark. Now will he sit under a medlar tree, And wish his mistress were that kind of fruit As maids call medlars when they laugh alone. O Romeo, that she were, O that sM An open-arse and thou a poperin pear! Romeo, good night. I ll to my truckle-bed. This field-bed is too cold for me to sleep. To seek him here that means not to be found. He jests at scars that never felt a wound. Juliet appears above at a window. But soft, what light through yonder window breaks? It is the east, and Juliet is the sun! Arise fair sun and kiM ll the envious moon, Who is already sick and pale with grief, That thou her maid art far more fair than she. Be not her maid since she is envious; Her vestal livery is but sick and green, And none but fools do wear it; cast it off. It is my lady, O it is my love! O, that she knew she were! She speaks, yet she says nothing. What of that? Her eye discourses, I will answer it. tis not to me she speaks. Two of the fairest stars in all the heaven, Having some business, do entreat her eyes To twinkle in their spheres till they return. What if her eyes were there, they in her head? The brightness of her cheek would shame those stars, As daylight doth a lamp; her eyes in heaven Would through the airy region stream so bright That birds would sing and think it were not night. See how she leans her cheek upon her hand. O that I were a glove upon that hand, That I might touch that cheek. O speak again bright angel, for thou art As glorious to this night, M As is a winged messenger of heaven Unto the white-upturned wondering eyes Of mortals that fall back to gaze on him When he bestrides the lazy-puffing clouds And sails upon the bosom of the air. O Romeo, Romeo, wherefore art thou Romeo? Deny thy father and refuse thy name. Or if thou wilt not, be but sworn my love, ll no longer be a Capulet. [_Aside._] Shall I hear more, or shall I speak at this? Tis but thy name that is my enemy; thyself, though not a Montague. s Montague? It is nor hand nor foot, Nor arm, nor face, nor any other part Belonging to a man. O be some other name. s in a name? That which we call a rose By any other name would smell as sweet; So Romeo would, were he not Romeo call Retain that dear perfection which he owes Without that title. Romeo, doff thy name, And for thy name, which is no part of thee, I take thee at thy word. Call me but love, and I Henceforth I never will be Romeo. What man art thou that, thus bescreen So stumblest on my counsel? I know not how to tell thee who I am: My name, dear saint, is hateful to myself, Because it is an enemy to thee. Had I it written, I would tear the word. My ears have yet not drunk a hundred words s utterance, yet I know the sound. Art thou not Romeo, and a Montague? Neither, fair maid, if either thee dislike. st thou hither, tell me, and wherefore? The orchard walls are high and hard to climb, And the place death, considering who thou art, If any of my kinsmen find thee here. s light wings did I o erperch these walls, For stony limits cannot hold love out, And what love can do, that dares love attempt: Therefore thy kinsmen are no stop to me. If they do see thee, they will murder thee. Alack, there lies more peril in thine eye Than twenty of their swordM s. Look thou but sweet, And I am proof against their enmity. I would not for the world they saw thee here. s cloak to hide me from their eyes, And but thou love me, let them find me here. My life were better ended by their hate Than death prorogued, wanting of thy love. By whose direction found st thou out this place? By love, that first did prompt me to enquire; He lent me counsel, and I lent him eyes. I am no pilot; yet wert thou as far d with the farthest sea, I should adventure for such merchandise. Thou knowest the mask of night is on my face, Else would a maiden blush bepaint my cheek For that which thou hast heard me speak tonight. Fain would I dwell on form, fain, fain deny What I have spoke; but farewell compliment. Dost thou love me? I know thou wilt say Ay, And I will take thy word. Yet, if thou swear Thou mayst prove false. At lovers They say Jove laughs. O gentle Romeo, u dost love, pronounce it faithfully. Or if thou thinkest I am too quickly won, ll frown and be perverse, and say thee nay, So thou wilt woo. But else, not for the world. In truth, fair Montague, I am too fond; And therefore thou mayst think my But trust me, gentleman, I Than those that have more cunning to be strange. I should have been more strange, I must confess, But that thou overheard My true-love passion; therefore pardon me, not impute this yielding to light love, Which the dark night hath so discovered. Lady, by yonder blessed moon I vow, That tips with silver all these fruit-tree tops, O swear not by the moon, th That monthly changes in her circled orb, Lest that thy love prove likewise variable. What shall I swear by? Do not swear at all. Or if thou wilt, swear by thy gracious self, Which is the god of my idolatry, Well, do not swear. Although I joy in thee, I have no joy of this contract tonight; It is too rash, too unadvis Too like the lightning, which doth cease to be Ere one can say It lightens. Sweet, good night. This bud of love, by summer May prove a beauteous flower when next we meet. Good night, good night. As sweet repose and rest Come to thy heart as that within my breast. O wilt thou leave me so unsatisfied? satisfaction canst thou have tonight? exchange of thy love s faithful vow for mine. I gave thee mine before thou didst request it; And yet I would it were to give again. st thou withdraw it? For what purpose, love? But to be frank and give it thee again. And yet I wish but for the thing I have; My bounty is as boundless as the sea, My love as deep; the more I give to thee, The more I have, for both are infinite. I hear some noise within. Dear love,M [_Nurse calls within._] Sweet Montague be true. Stay but a little, I will come again. O blessed, blessed night. I am afeard, Being in night, all this is but a dream, Too flattering sweet to be substantial. Enter Juliet above. Three words, dear Romeo, and good night indeed. If that thy bent of love be honourable, Thy purpose marriage, send me word tomorrow, ll procure to come to thee, Where and what time thou wilt perform tM And all my fortunes at thy foot I And follow thee my lord throughout the world. But if thou meanest not well, To cease thy strife and leave me to my grief. Tomorrow will I send. A thousand times good night. A thousand times the worse, to want thy light. Love goes toward love as schoM olboys from their books, But love from love, towards school with heavy looks. [_Retiring slowly._] Re-enter Juliet, above. Hist! Romeo, hist! O for a falconer To lure this tassel-gentle back again. Bondage is hoarse and may not speak aloud, Else would I tear the cave where Echo lies, And make her airy tongue more hoarse than mine With repetition of my Romeo It is my soul that calls upon my name. How silver-sweet sound lovers t music to attending ears. Shall I send to thee? By the hour of nine. Tis twenty years till then. I have forgot why I did call thee back. Let me stand here till thou remember it. I shall forget, to have thee still stand there, Remembering how I love thy company. ll still stay, to have thee still forget, Forgetting any other home but this. Tis almost morning; I would have thee gone, And yet no farther than a wanton That lets it hop a little from her hand, Like a poor prisoner in his twisted gyves, And with a silk thread plucks it back again, So loving-jealous of his liberty. I would I were thy bird. Yet I should kill thee with much cherishing. Good night, good night. Parting is such sweet sorrow That I shall say good night till it be morrow. Sleep dwell upon thine eyM es, peace in thy breast. Would I were sleep and peace, so sweet to rest. d morn smiles on the frowning night, Chequering the eastern clouds with streaks of light; And darkness fleckled like a drunkard reels s pathway, made by Titan Hence will I to my ghostly Sire His help to crave and my dear hap to tell. SCENE III. Friar Lawrence Enter Friar Lawrence with a basket. Now, ere the sun advance his burning eye,M The day to cheer, and night I must upfill this osier cage of ours With baleful weeds and precious-juiced flowers. s mother, is her tomb; What is her burying grave, that is her womb: And from her womb children of divers kind We sucking on her natural bosom find. Many for many virtues excellent, None but for some, and yet all different. O, mickle is the powerful grace that lies In plants, herbs, stones, and their true qualities. For naught so vile that on M But to the earth some special good doth give; Nor aught so good but, strain d from that fair use, Revolts from true birth, stumbling on abuse. Virtue itself turns vice being misapplied, s by action dignified. Within the infant rind of this weak flower Poison hath residence, and medicine power: For this, being smelt, with that part cheers each part; Being tasted, slays all senses with the heart. Two such opposed kings encamp them still grace and rude will; And where the worser is predominant, Full soon the canker death eats up that plant. Good morrow, father. What early tongue so sweet saluteth me? Young son, it argues a distemper So soon to bid good morrow to thy bed. Care keeps his watch in every old man And where care lodges sleep will never lie; But where unbruised youth with unstuff Doth couch his limbs, there golden sleep doth reign. thy earliness doth me assure d with some distemperature; Or if not so, then here I hit it right, Our Romeo hath not been in bed tonight. That last is true; the sweeter rest was mine. God pardon sin. Wast thou with Rosaline? With Rosaline, my ghostly father? No. I have forgot that name, and that name s my good son. But where hast thou been then? ll tell thee ere thou ask it me again. ing with mine enemy, Where on a sudden one hath wounded me s by me wounded. Both our remedies Within thy help and holy physic lies. I bear no hatred, blessed man; for lo, My intercession likewise steads my foe. Be plain, good son, and homely in thy drift; Riddling confession finds but riddling shrift. Then plainly know my heart On the fair daughter of rich Capulet. As mine on hers, so hers is set on mine; d, save what thou must comM By holy marriage. When, and where, and how d, and made exchange of vow, ll tell thee as we pass; but this I pray, That thou consent to marry us today. Holy Saint Francis! What a change is here! Is Rosaline, that thou didst love so dear, So soon forsaken? Young men Not truly in their hearts, but in their eyes. Jesu Maria, what a deal of brine d thy sallow cheeks for Rosaline! How much salt water thrown away in waste, ve, that of it doth not taste. The sun not yet thy sighs from heaven clears, Thy old groans yet ring in mine ancient ears. Lo here upon thy cheek the stain doth sit Of an old tear that is not wash If ere thou wast thyself, and these woes thine, Thou and these woes were all for Rosaline, d? Pronounce this sentence then, Women may fall, when there s no strength in men. st me oft for loving Rosaline. For doting, not for loving, pupM To lay one in, another out to have. I pray thee chide me not, her I love now Doth grace for grace and love for love allow. The other did not so. Thy love did read by rote, that could not spell. But come young waverer, come go with me, ll thy assistant be; For this alliance may so happy prove, To turn your households rancour to pure love. hence; I stand on sudden haste. Wisely and slow; they stumble that run fast. Enter Benvolio and Mercutio. Where the devil should this Romeo be? Came he not home tonight? s; I spoke with his man. Why, that same pale hard-hearted wench, that Rosaline, torments him so that he will sure run mad. Tybalt, the kinsman to old Capulet, hath sent a letter to his father A challenge, on my life. Romeo will answer it. Any man that can write may answer a letter. Nay, he will answer the letter s master, how he dares, being dared. Alas poor Romeo, he is already dead, stabbed with a white wench eye; run through the ear with a love song, the very pin of his heart cleft with the blind bow-boy s butt-shaft. And is he a man to encounter Why, what is Tybalt? s the courageous captain of compliments. He fights as you sing prick-song, keeps time, distance, and proportion. He rests his minim rest, one, two, and the third in your bosom: the very butcher of a silk button, a duellist, a duellist; a gentleman of the very first house, of the first and second cause. Ah, the immortal passado, the punto reverso, the hay. The pox of such antic lisping, affecting phantasies; these new tuners of accent. By Jesu, a very good M blade, a very tall man, a very good whore. Why, is not this a lamentable thing, grandsire, that we should be thus afflicted with these strange flies, these fashion-mongers, s, who stand so much on the new form that they cannot sit at ease on the old bench? O their bones, their bones! Here comes Romeo, here comes Romeo! Without his roe, like a dried herring. O flesh, flesh, how art thou fishified! Now is he for the numbers that Petrarch flowed in. LM his lady, was but a kitchen wench, marry, she had a better love to berhyme her: Dido a dowdy; Cleopatra a gypsy; Helen and Hero hildings and harlots; Thisbe a grey eye or so, but not to the purpose. Signior Romeo, bonjour! There s a French salutation to your French slop. You gave us the counterfeit fairly last night. Good morrow to you both. What counterfeit did I give you? The slip sir, the slip; can you not conceive? Pardon, good Mercutio, my business was greaM t, and in such a case as mine a man may strain courtesy. s as much as to say, such a case as yours constrains a man to bow Thou hast most kindly hit it. A most courteous exposition. Nay, I am the very pink of courtesy. Why, then is my pump well flowered. Sure wit, follow me this jest now, till thou hast worn out thy pump, ingle sole of it is worn, the jest may remain after the wearing, solely singular. O single-soled jest, solely singular for the singleness! Come between us, good Benvolio; my wits faint. Swits and spurs, swits and spurs; or I Nay, if thy wits run the wild-goose chase, I am done. For thou hast more of the wild-goose in one of thy wits, than I am sure, I have in my whole five. Was I with you there for the goose? Thou wast never with me fM or anything, when thou wast not there for the I will bite thee by the ear for that jest. Nay, good goose, bite not. Thy wit is a very bitter sweeting, it is a most sharp sauce. And is it not then well served in to a sweet goose? s a wit of cheveril, that stretches from an inch narrow to an I stretch it out for that word broad, which added to the goose, proves thee far and wide a broad goose. s not this better now than groaning for love? Now art thou sociable, now art thou Romeo; not art thou what thou art, by art as well as by nature. For this drivelling love is like a great natural, that runs lolling up and down to hide his bauble in a hole. Stop there, stop there. Thou desirest me to stop in my tale against the hair. Thou wouldst else have made thy tale large. O, thou art deceived; I would have made it short, for I was come to the h of my tale, and meant indeed to occupy the argument no Enter Nurse and Peter. Two, two; a shirt and a smock. Good Peter, to hide her face; for her fan God ye good morrow, gentlemen. God ye good-den, fair gentlewoman. Tis no less, I tell ye; for the bawdy hand of the dial is nM Out upon you! What a man are you? One, gentlewoman, that God hath made for himself to mar. By my troth, it is well said; for himself to mar, quoth a? Gentlemen, can any of you tell me where I may find the young Romeo? I can tell you: but young Romeo will be older when you have found him than he was when you sought him. I am the youngest of that name, for Yea, is the worst well? Very weM faith; wisely, wisely. If you be he, sir, I desire some confidence with you. She will endite him to some supper. A bawd, a bawd, a bawd! So ho! What hast thou found? No hare, sir; unless a hare, sir, in a lenten pie, that is something stale and hoar ere it be spent. An old hare hoar, And an old hare hoar, Is very good meat in Lent; But a hare that is hoar Is too much for a score When it hoars ere it bM Romeo, will you come to your father ll to dinner thither. Farewell, ancient lady; farewell, lady, lady, lady. [_Exeunt Mercutio and Benvolio._] I pray you, sir, what saucy merchant was this that was so full of his A gentleman, Nurse, that loves to hear himself talk, and will speak more in a minute than he will stand to in a month. And a speak anything against me, I ll take him down, and a were lustier han he is, and twenty such Jacks. And if I cannot, I that shall. Scurvy knave! I am none of his flirt-gills; I am none of And thou must stand by too and suffer every knave to use me at his pleasure! I saw no man use you at his pleasure; if I had, my weapon should quickly have been out. I warrant you, I dare draw as soon as another man, if I see occasion in a good quarrel, and the law on my side. Now, afore God, I am so vexed that every part about me quiM knave. Pray you, sir, a word: and as I told you, my young lady bid me enquire you out; what she bade me say, I will keep to myself. But first let me tell ye, if ye should lead her in a fool say, it were a very gross kind of behaviour, as they say; for the gentlewoman is young. And therefore, if you should deal double with her, truly it were an ill thing to be offered to any gentlewoman, and ROMEO. Nurse, commend me to thy lady and mistress. I protest uM faith I will tell her as much. Lord, Lord, she will What wilt thou tell her, Nurse? Thou dost not mark me. I will tell her, sir, that you do protest, which, as I take it, is a gentlemanlike offer. Some means to come to shrift this afternoon, And there she shall at Friar Lawrence d and married. Here is for thy pains. No truly, sir; not a penny. This afternoon, sir? Well, she shall be there. And stay, good Nurse, behind the abbey wall. Within this hour my man shall be with thee, And bring thee cords made like a tackled stair, Which to the high topgallant of my joy Must be my convoy in the secret night. Farewell, be trusty, and I Farewell; commend me to thy mistress. Now God in heaven bless thee. Hark you, sir. st thou, my dear Nurse? Is your man secret? DidM Two may keep counsel, putting one away? I warrant thee my man Well, sir, my mistress is the sweetest lady. Lord, Lord! When little prating thing, O, there is a nobleman in town, one Paris, that would fain lay knife aboard; but she, good soul, had as lief see a toad, a very toad, as see him. I anger her sometimes, and tell her that Paris is the properer man, but I ll warrant you, when I say so, she looks as pale as any clout in thM e versal world. Doth not rosemary and Romeo begin both with a letter? Ay, Nurse; what of that? Both with an R. s name. R is for the no, I know it begins with some other letter, and she hath the prettiest sententious of it, of you and rosemary, that it would do you good to hear it. Commend me to thy lady. Ay, a thousand times. Peter! The clock struck nine when I did send the Nurse, In half an hour she promised to return. Perchance she cannot meet him. That O, she is lame. Love s heralds should be thoughts, Which ten times faster glides than the sun Driving back shadows over lowering hills: Therefore do nimble-pinion And therefore hath the wind-swift Cupid wings. Now is the sun upon the highmost hill s journey, and from nine till twelve s three long hours, yet she is not come. Had she affections and warm youthful blood, d be as swift in motion as a ball; My words would bandy her to my sweet love, But old folks, many feign as they were dead; Unwieldy, slow, heavy and pale as lead. Enter Nurse and Peter. O God, she comes. O honey Nurse, what news? Hast thou met with him? Send thy man away. Peter, stay at the gate. Now, good sweet Nurse, hough news be sad, yet tell them merrily; st the music of sweet news By playing it to me with so sour a face. I am aweary, give me leave awhile; Fie, how my bones ache! What a jaunt have I had! I would thou hadst my bones, and I thy news: Nay come, I pray thee speak; good, good Nurse, speak. Jesu, what haste? Can you not stay a while? Do you not see that I am How art thou out of breath, when thou hast breath To say to me that thM ou art out of breath? The excuse that thou dost make in this delay Is longer than the tale thou dost excuse. Is thy news good or bad? Answer to that; ll stay the circumstance. Let me be satisfied, is Well, you have made a simple choice; you know not how to choose a man. Romeo? No, not he. Though his face be better than any man s, and for a hand and a foot, and a body, though they be not to be talked on, yet they are past compaM flower of courtesy, but I ll warrant him as gentle as a lamb. Go thy ways, wench, serve God. What, have you dined at home? No, no. But all this did I know before. What says he of our marriage? What of that? Lord, how my head aches! What a head have I! It beats as it would fall in twenty pieces. Beshrew your heart for sending me about To catch my death with jauncing up and down. faith, I am sorry thatM Sweet, sweet, sweet Nurse, tell me, what says my love? Your love says like an honest gentleman, And a courteous, and a kind, and a handsome, And I warrant a virtuous, Where is your mother? Where is my mother? Why, she is within. Where should she be? How oddly thou repliest. Your love says, like an honest gentleman, Where is your mother? Are you so hot? Marry, come up, I trow. Is this the poultice for my aching bones? eforward do your messages yourself. s such a coil. Come, what says Romeo? Have you got leave to go to shrift today? Then hie you hence to Friar Lawrence There stays a husband to make you a wife. Now comes the wanton blood up in your cheeks, ll be in scarlet straight at any news. Hie you to church. I must another way, To fetch a ladder by the which your love s nest soon when it is dark. I am the drudge, and toil iM But you shall bear the burden soon at night. ll to dinner; hie you to the cell. Hie to high fortune! Honest Nurse, farewell. SCENE VI. Friar Lawrence Enter Friar Lawrence and Romeo. So smile the heavens upon this holy act That after-hours with sorrow chide us not. Amen, amen, but come what sorrow can, It cannot countervail the exchange of joy That one short minute gives me in her sight. Do thou but close our haM nds with holy words, Then love-devouring death do what he dare, It is enough I may but call her mine. These violent delights have violent ends, And in their triumph die; like fire and powder, Which as they kiss consume. The sweetest honey Is loathsome in his own deliciousness, And in the taste confounds the appetite. Therefore love moderately: long love doth so; Too swift arrives as tardy as too slow. Here comes the lady. O, so light a foot A lover may bestride the gossamers That idles in the wanton summer air And yet not fall; so light is vanity. Good even to my ghostly confessor. Romeo shall thank thee, daughter, for us both. As much to him, else is his thanks too much. Ah, Juliet, if the measure of thy joy d like mine, and that thy skill be more To blazon it, then sweeten with thy breath This neighbour air, and let rich music happiness that both Receive in either by this dear encounter. Conceit more rich in matter than in words, Brags of his substance, not of ornament. They are but beggars that can count their worth; But my true love is grown to such excess, I cannot sum up sum of half my wealth. Come, come with me, and we will make short work, For, by your leaves, you shall not stay alone Till holy church incorporate two in one. SCENE I. A public Place. Mercutio, Benvolio, Page and Servants. I pray thee, good Mercutio, let The day is hot, the Capulets abroad, And if we meet, we shall not scape a brawl, For now these hot days, is the mad blood stirring. Thou art like one of these fellows that, when he enters the confines of a tavern, claps me his sword upon the table, and says and by the operation of the second cup draws him on the drawer, when indeed there is no need. m I like such a fellow? Come, come, thou art as hot a Jack in thy mood as any in Italy; and as soon moved to be moody, and as soon moody to be moved. Nay, an there were two such, we should have none shortly, for one would kill the other. Thou? Why, thou wilt quarrel with a man that hath a hair more or a hair less in his beard than thou hast. Thou wilt quarrel with a man for cracking nuts, having no other reason but because thou hast hazel eyes. What eye buM t such an eye would spy out such a quarrel? Thy head is as full of quarrels as an egg is full of meat, and yet thy head hath been beaten as addle as an egg for quarrelling. Thou hast quarrelled with a man for coughing in the street, because he hath wakened thy dog that hath lain asleep in the sun. Didst thou not fall out with a tailor for wearing his new doublet before Easter? with another for tying his new shoes with an old riband? And yet thou wilt tutor me from quarrelling! to quarrel as thou art, any man should buy the fee simple of my life for an hour and a quarter. The fee simple! O simple! Enter Tybalt and others. By my head, here comes the Capulets. By my heel, I care not. Follow me close, for I will speak to them. Gentlemen, good-den: a word with one of you. And but one word with one of us? Couple it with something; make it a You shall find me apt enough to that, sir, and youM Could you not take some occasion without giving? Mercutio, thou consortest with Romeo. Consort? What, dost thou make us minstrels? And thou make minstrels of us, look to hear nothing but discords. Here s my fiddlestick, here that shall make you dance. Zounds, consort! We talk here in the public haunt of men. Either withdraw unto some private place, And reason coldly of your grievances, Or else depart; here all eyes gaze on us.M s eyes were made to look, and let them gaze. I will not budge for no man Well, peace be with you, sir, here comes my man. ll be hanged, sir, if he wear your livery. Marry, go before to field, he ll be your follower; Your worship in that sense may call him man. Romeo, the love I bear thee can afford No better term than this: Thou art a villain. Tybalt, the reason that I have to love thee xcuse the appertaining rage To such a greeting. Villain am I none; Therefore farewell; I see thou know Boy, this shall not excuse the injuries That thou hast done me, therefore turn and draw. I do protest I never injur But love thee better than thou canst devise Till thou shalt know the reason of my love. And so good Capulet, which name I tender As dearly as mine own, be satisfied. O calm, dishonourable, vile submission! [_Draws._] Alla stoccata caM Tybalt, you rat-catcher, will you walk? What wouldst thou have with me? Good King of Cats, nothing but one of your nine lives; that I mean to make bold withal, and, as you shall use me hereafter, dry-beat the rest of the eight. Will you pluck your sword out of his pilcher by the ears? Make haste, lest mine be about your ears ere it be out. [_Drawing._] I am for you. Gentle Mercutio, put thy rapier up. Come, sir, your passado. Draw, Benvolio; beat down their weapons. Gentlemen, for shame, forbear this outrage, Tybalt, Mercutio, the Prince expressly hath Forbid this bandying in Verona streets. Hold, Tybalt! Good Mercutio! [_Exeunt Tybalt with his Partizans._] both your houses. I am sped. Is he gone, and hath nothing? What, art thou hurt? Ay, ay, a scratch, a scratch. Marry, Where is my page? Go villain, fetch a surgeon. Courage, man; the hurt cannot be much. tis not so deep as a well, nor so wide as a church door, but twill serve. Ask for me tomorrow, and you shall find me a grave man. I am peppered, I warrant, for this world. A plague o your houses. Zounds, a dog, a rat, a mouse, a cat, to scratch a man to death. A braggart, a rogue, a villain, that fights by the book of Why the devil came you between us? I was hurt under your I thought all for the best. Help me into some house, Benvolio, Or I shall faint. A plague o They have made worms I have it, and soundly too. Your houses! [_Exeunt Mercutio and Benvolio._] This gentleman, the Prince My very friend, hath got his mortal hurt In my behalf; my reputation stain Tybalt, that an hour Hath been my cousin. O sweet Juliet, Thy beauty hath made me effeminate O Romeo, Romeo, brave Mercutio That gallant spirit hath aspir Which too untimely here did scorn the earth. s black fate on mo days doth depend; This but begins the woe others must end. Here comes the furious Tybalt back again. Again in triumph, and Mercutio slain? Away to heaven respective lenity, d fury be my conduct now! Is but a little way above our heads, Staying for thine to keep him company. Either thou or I, or both, must go with him. Thou wretched boy, that didst consort him here, Shalt with him hence. This shall determine that. [_They fight; Tybalt falls._] Romeo, away, be gone! The citizens are up, and Tybalt slain. d. The Prince will doom thee death If thou art taken. HencM Which way ran he that kill Tybalt, that murderer, which way ran he? There lies that Tybalt. Up, sir, go with me. I charge thee in the Prince Enter Prince, attended; Montague, Capulet, their Wives and others. Where are the vile beginners of this fray? O noble Prince, I can discoM The unlucky manage of this fatal brawl. There lies the man, slain by young Romeo, That slew thy kinsman, brave Mercutio. Tybalt, my cousin! O my brother O Prince! O husband! O, the blood is spill Of my dear kinsman! Prince, as thou art true, For blood of ours shed blood of Montague. Benvolio, who began this bloody fray? Tybalt, here slain, whom Romeo Romeo, that spoke him fair, bid him bethink he quarrel was, and urg Your high displeasure. All this uttered With gentle breath, calm look, knees humbly bow Could not take truce with the unruly spleen Of Tybalt, deaf to peace, but that he tilts With piercing steel at bold Mercutio Who, all as hot, turns deadly point to point, And, with a martial scorn, with one hand beats Cold death aside, and with the other sends It back to Tybalt, whose dexterity Retorts it. Romeo he cries aloud, Hold, friends! Friends, part! swifter than his tongue, His agile arm beats down their fatal points, twixt them rushes; underneath whose arm An envious thrust from Tybalt hit the life Of stout Mercutio, and then Tybalt fled. But by and by comes back to Romeo, Who had but newly entertain t they go like lightning; for, ere I Could draw to part them was stout Tybalt slain; And as he fell did Romeo turn and fly. This is the truth, or let Benvolio die. He is a kinsman to the Montague. n makes him false, he speaks not true. Some twenty of them fought in this black strife, And all those twenty could but kill one life. I beg for justice, which thou, Prince, must give; Romeo slew Tybalt, Romeo must not live. Romeo slew him, he slew Mercutio. Who now the price of his dear blood doth owe? Not Romeo, Prince, he was Mercutio His fault concludes but what the law should end, And for that offence Immediately we do exile him henceM I have an interest in your hate My blood for your rude brawls doth lie a-bleeding. ll amerce you with so strong a fine That you shall all repent the loss of mine. I will be deaf to pleading and excuses; Nor tears nor prayers shall purchase out abuses. Therefore use none. Let Romeo hence in haste, Else, when he is found, that hour is his last. Bear hence this body, and attend our will. Mercy but murders, pardoning those that kill. SCENE II. A Room in Capulet Gallop apace, you fiery-footed steeds, lodging. Such a waggoner As Phaeton would whip you to the west And bring in cloudy night immediately. Spread thy close curtain, love-performing night, s eyes may wink, and Romeo Leap to these arms, untalk Lovers can see to do their amorous rites By their own beauties: or, if love be blind, It best agrees with night. Come, civil night, Thou sober-suited matron, all in black, d learn me how to lose a winning match, d for a pair of stainless maidenhoods. d blood, bating in my cheeks, With thy black mantle, till strange love, grow bold, Think true love acted simple modesty. Come, night, come Romeo; come, thou day in night; For thou wilt lie upon the wings of night Whiter than new snow upon a raven Come gentle night, come loving black-brow Give me my Romeo, and when I shall die, Take him and cut him out in little stars, ke the face of heaven so fine That all the world will be in love with night, And pay no worship to the garish sun. O, I have bought the mansion of a love, d it; and though I am sold, d. So tedious is this day As is the night before some festival To an impatient child that hath new robes And may not wear them. O, here comes my Nurse, And she brings news, and every tongue that speaks s name speaks heavenly eloquence. Enter Nurse, with cords. hat news? What hast thou there? The cords that Romeo bid thee fetch? [_Throws them down._] Ay me, what news? Why dost thou wring thy hands? We are undone, lady, we are undone. Can heaven be so envious? Though heaven cannot. O Romeo, Romeo. Who ever would have thought it? Romeo! What devil art thou,M that dost torment me thus? This torture should be roar Hath Romeo slain himself? Say thou but Ay, And that bare vowel I shall poison more Than the death-darting eye of cockatrice. I am not I if there be such an I; Or those eyes shut that make thee answer Ay. If he be slain, say Ay; or if not, No. Brief sounds determine of my weal or woe. I saw the wound, I saw it with mine eyes, here on his manly breast. A piteous corse, a bloody piteous corse; ale as ashes, all bedaub All in gore-blood. I swounded at the sight. O, break, my heart. Poor bankrout, break at once. Vile earth to earth resign; end motion here, And thou and Romeo press one heavy bier. O Tybalt, Tybalt, the best friend I had. O courteous Tybalt, honest gentleman! That ever I should live to see thee dead. What storm is this that blows so contrary? d and is Tybalt dead? t cousin, and my dearer lord? Then dreadful trumpet sound the general doom, For who is living, if those two are gone? Tybalt is gone, and Romeo banished, d him, he is banished. It did, it did; alas the day, it did. O serpent heart, hid with a flowering face! Did ever dragon keep so fair a cave? Beautiful tyrant, fiend angelical, d raven, wolvish-ravening lamb! Despised substance of M Just opposite to what thou justly seem A damned saint, an honourable villain! O nature, what hadst thou to do in hell When thou didst bower the spirit of a fiend In mortal paradise of such sweet flesh? Was ever book containing such vile matter So fairly bound? O, that deceit should dwell In such a gorgeous palace. No faith, no honesty in men. All perjur All forsworn, all naught, all dissemblers. s my man? Give me some aqua vitae. griefs, these woes, these sorrows make me old. Shame come to Romeo. For such a wish! He was not born to shame. Upon his brow shame is asham tis a throne where honour may be crown Sole monarch of the universal earth. O, what a beast was I to chide at him! Will you speak well of him that kill Shall I speak ill of him that is my husband? Ah, poor my lord, what tongue shall smooth thy name, When I thy three-houM wife have mangled it? But wherefore, villain, didst thou kill my cousin? That villain cousin would have kill Back, foolish tears, back to your native spring, Your tributary drops belong to woe, Which you mistaking offer up to joy. My husband lives, that Tybalt would have slain, s dead, that would have slain my husband. All this is comfort; wherefore weep I then? Some word there was, worser than Tybalt d me. I would forget it fain, Like damned guilty deeds to sinners Tybalt is dead, and Romeo banished. Hath slain ten thousand Tybalts. Tybalt Was woe enough, if it had ended there. Or if sour woe delights in fellowship, And needly will be rank d with other griefs, d not, when she said Tybalt Thy father or thy mother, nay or both, Which modern lamentation might have mov But with a rear-ward following Tybalt Is father, mother, Tybalt, Romeo, Juliet, All slain, all dead. Romeo is banished, There is no end, no limit, measure, bound, s death, no words can that woe sound. Where is my father and my mother, Nurse? Weeping and wailing over Tybalt Will you go to them? I will bring you thither. Wash they his wounds with tears. Mine shall be spent, When theirs are dry, for Romeo Take up those cords. Poor ropes, M Both you and I; for Romeo is exil He made you for a highway to my bed, But I, a maid, die maiden-widowed. Come cords, come Nurse, I ll to my wedding bed, And death, not Romeo, take my maidenhead. Hie to your chamber. I To comfort you. I wot well where he is. Hark ye, your Romeo will be here at night. ll to him, he is hid at Lawrence O find him, give this ring to my true knight, And bid him come to take his last farewell. SCENE III. Friar Lawrence Enter Friar Lawrence. Romeo, come forth; come forth, thou fearful man. Affliction is enanmour And thou art wedded to calamity. Father, what news? What is the Prince What sorrow craves acquaintance at my hand, That I yet know not? Is my dear son with such sour company. I bring thee tidings of the Prince What less than doomsday is thM A gentler judgment vanish Ha, banishment? Be merciful, say death; For exile hath more terror in his look, Much more than death. Do not say banishment. Hence from Verona art thou banished. Be patient, for the world is broad and wide. There is no world without Verona walls, But purgatory, torture, hell itself. Hence banished is banish exile is death. Then banished d. Calling death banished, st my head off with a golden axe, And smilest upon the stroke that murders me. O deadly sin, O rude unthankfulness! Thy fault our law calls death, but the kind Prince, Taking thy part, hath brush d that black word death to banishment. This is dear mercy, and thou see Tis torture, and not mercy. Heaven is here Where Juliet lives, and every cat aM And little mouse, every unworthy thing, Live here in heaven and may look on her, But Romeo may not. More validity, More honourable state, more courtship lives In carrion flies than Romeo. They may seize On the white wonder of dear Juliet And steal immortal blessing from her lips, Who, even in pure and vestal modesty Still blush, as thinking their own kisses sin. But Romeo may not, he is banished. This may flies do, when I from this must fly. They are free men but I am banished. st thou yet that exile is not death? Hadst thou no poison mix d, no sharp-ground knife, No sudden mean of death, though ne But banished to kill me? Banished? O Friar, the damned use that word in hell. Howlings attends it. How hast thou the heart, Being a divine, a ghostly confessor, A sin-absolver, and my friend profess To mangle me with that word banished? Thou fond mad man, hear me speak a little, O, thou wilt speak again of banishment. ll give thee armour to keep off that word, s sweet milk, philosophy, To comfort thee, though thou art banished. Yet banished? Hang up philosophy. Unless philosophy can make a Juliet, Displant a town, reverse a Prince It helps not, it prevails not, talk no more. O, then I see that mad men have no ears. How should they, when that wise men have no eyes? Let me dispute with thee of thy estate. peak of that thou dost not feel. Wert thou as young as I, Juliet thy love, An hour but married, Tybalt murdered, Doting like me, and like me banished, Then mightst thou speak, then mightst thou tear thy hair, And fall upon the ground as I do now, Taking the measure of an unmade grave. [_Knocking within._] Arise; one knocks. Good Romeo, hide thyself. Not I, unless the breath of heartsick groans Mist-like infold me from the search of eyes. Hark, how they knock! What simpleness is this. Who knocks so hard? Whence come you, what [_Within._] Let me come in, and you shall know my errand. I come from Lady Juliet. O holy Friar, O, tell me, holy Friar, There on the ground, with his own tears made drunk. O, he is even in my mistress Just in her case! O woeful sympathy! Piteous predicament. Even so lies she, Blubbering and weeping, weeping and blubbering. Stand up, stand up; stand, and you be a man. s sake, for her sake, rise and stand. Why should you fall into so deep an O? Ah sir, ah sir, death Spakest thou of Juliet? How is it withM Doth not she think me an old murderer, d the childhood of our joy d but little from her own? Where is she? And how doth she? And what says d lady to our cancell O, she says nothing, sir, but weeps and weeps; And now falls on her bed, and then starts up, And Tybalt calls, and then on Romeo cries, And then down falls again. Shot from the deadly level of a gun, Did murder her, as that name d her kinsman. O, tell me, Friar, tell me, In what vile part of this anatomy Doth my name lodge? Tell me, that I may sack The hateful mansion. [_Drawing his sword._] Hold thy desperate hand. Art thou a man? Thy form cries out thou art. Thy tears are womanish, thy wild acts denote The unreasonable fury of a beast. Unseemly woman in a seeming man, And ill-beseeming beast in seeming both! d me. By my holy order, I thought thy disposition better temper Hast thou slain Tybalt? Wilt thou slay thyself? And slay thy lady, that in thy life lives, By doing damned hate upon thyself? st thou on thy birth, the heaven and earth? Since birth, and heaven and earth, all three do meet In thee at once; which thou at once wouldst lose. st thy shape, thy love, thy wit, Which, like a usurer, abound And usest none in that true use indeed Which should bedeck thy shape, thy love, thy wit. Thy noble shape is but a form of wax, Digressing from the valour of a man; Thy dear love sworn but hollow perjury, Killing that love which thou hast vow Thy wit, that ornament to shape and love, Misshapen in the conduct of them both, Like powder in a skilless soldier Is set afire by thine own ignorance, d with thine own defence. What, rouse thee, man. Thy Juliet is alive, For whose dear sake thou wast but lately dead. There art thou happy. Tybalt would kill thee, The law that threaten d death becomes thy friend, And turns it to exile; there art thou happy. A pack of blessings light upon thy back; Happiness courts thee in her best array; But like a misshaped and sullen wench, st up thy Fortune and thy love. Take heed, take heed, for such die miserable. Go, get thee to thy love as was decreed, Ascend her chamber, hence and comfort her. But look thou stay not till the watch be set, For then thou canst not pass to Mantua; shalt live till we can find a time To blaze your marriage, reconcile your friends, Beg pardon of the Prince, and call thee back With twenty hundred thousand times more joy st forth in lamentation. Go before, Nurse. Commend me to thy lady, And bid her hasten all the house to bed, Which heavy sorrow makes them apt unto. O Lord, I could have stay d here all the night To hear good counsel. O, what learning is! ll tell my lady you will come. Do so, and bid my sweet prepare to chide. Here sir, a ring she bid me give you, sir. Hie you, make haste, for it grows very late. How well my comfort is reviv Go hence, good night, and here stands all your state: Either be gone before the watch be set, Or by the break of day disguis Sojourn in Mantua. I ll find out your man, And he shall signify from time to time Every good hap to you that chances here. tis late; farewell; good night. But that a joy past joy calls out on me, It were a grief so brief to part with thee. SCENE IV. A Room in Capulet Enter Capulet, Lady Capulet and Paris. Things have fallen out, sir, so unluckily That we have had no time to move our daughter. d her kinsman Tybalt dearly, And so did I. Well, we were born to die. ll not come down tonight. I promise you, but for your M I would have been abed an hour ago. These times of woe afford no tune to woo. Madam, good night. Commend me to your daughter. I will, and know her mind early tomorrow; d up to her heaviness. Sir Paris, I will make a desperate tender s love. I think she will be rul In all respects by me; nay more, I doubt it not. Wife, go you to her ere you go to bed, Acquaint her here of my son Paris And bid her, mark you me,M But, soft, what day is this? Monday! Ha, ha! Well, Wednesday is too soon, A Thursday let it be; a Thursday, tell her, She shall be married to this noble earl. Will you be ready? Do you like this haste? ll keep no great ado, For, hark you, Tybalt being slain so late, It may be thought we held him carelessly, Being our kinsman, if we revel much. ll have some half a dozen friends, And there an end. But what saM My lord, I would that Thursday were tomorrow. Well, get you gone. A Thursday be it then. Go you to Juliet ere you go to bed, Prepare her, wife, against this wedding day. Light to my chamber, ho! Afore me, it is so very very late that we May call it early by and by. Good night. SCENE V. An open Gallery to Juliet s Chamber, overlooking the Garden. Enter Romeo and Juliet. Wilt thou be gone? It is not yet near day.M It was the nightingale, and not the lark, d the fearful hollow of thine ear; Nightly she sings on yond pomegranate tree. Believe me, love, it was the nightingale. It was the lark, the herald of the morn, No nightingale. Look, love, what envious streaks Do lace the severing clouds in yonder east. s candles are burnt out, and jocund day Stands tiptoe on the misty mountain tops. I must be gone and live, or stay and die. Yond light is not daylight, I know it, I. is some meteor that the sun exhales To be to thee this night a torchbearer And light thee on thy way to Mantua. Therefore stay yet, thou need en, let me be put to death, I am content, so thou wilt have it so. ll say yon grey is not the morning Tis but the pale reflex of Cynthia Nor that is not the lark whose notes do beat The vaulty heaven so high above our heads. I have more care to stay than will to go. Come, death, and welcome. JM s talk. It is not day. It is, it is! Hie hence, be gone, away. It is the lark that sings so out of tune, Straining harsh discords and unpleasing sharps. Some say the lark makes sweet division; This doth not so, for she divideth us. Some say the lark and loathed toad change eyes. O, now I would they had chang Since arm from arm that voice doth us affray, Hunting thee hence with hunt O now be gone, more light and liM More light and light, more dark and dark our woes. Your lady mother is coming to your chamber. The day is broke, be wary, look about. Then, window, let day in, and let life out. Farewell, farewell, one kiss, and I Art thou gone so? Love, lord, ay husband, friend, I must hear from thee every day in the hour, For in a minute there are many days. O, by this count I shall be much in years Ere I again behold my Romeo. I will omit no opportunity That may convey my greetings, love, to thee. O thinkest thou we shall ever meet again? I doubt it not, and all these woes shall serve For sweet discourses in our time to come. O God! I have an ill-divining soul! Methinks I see thee, now thou art so low, As one dead in the bottom of a tomb. Either my eyesight fails, or thou look ust me, love, in my eye so do you. Dry sorrow drinks our blood. Adieu, adieu. O Fortune, Fortune! All men call thee fickle, If thou art fickle, what dost thou with him d for faith? Be fickle, Fortune; For then, I hope thou wilt not keep him long [_Within._] Ho, daughter, are you up? t that calls? Is it my lady mother? Is she not down so late, or up so early? d cause procures her hitherM Enter Lady Capulet. Why, how now, Juliet? Madam, I am not well. Evermore weeping for your cousin What, wilt thou wash him from his grave with tears? And if thou couldst, thou couldst not make him live. Therefore have done: some grief shows much of love, But much of grief shows still some want of wit. Yet let me weep for such a feeling loss. So shall you feel the loss, but not the friend Feeling so the loss, I cannot choose but ever weep the friend. Well, girl, thou weep st not so much for his death As that the villain lives which slaughter What villain, madam? That same villain Romeo. Villain and he be many miles asunder. God pardon him. I do, with all my heart. And yet no man like he doth grieve my heart. That is because the traitor murderer lives. Ay madam, from the reach of these my hands.M Would none but I might venge my cousin We will have vengeance for it, fear thou not. Then weep no more. I ll send to one in Mantua, Where that same banish d runagate doth live, Shall give him such an unaccustom That he shall soon keep Tybalt company: And then I hope thou wilt be satisfied. Indeed I never shall be satisfied With Romeo till I behold him Is my poor heart so for a kinsman vex Madam, if you could find out but a man poison, I would temper it, That Romeo should upon receipt thereof, Soon sleep in quiet. O, how my heart abhors d, and cannot come to him, To wreak the love I bore my cousin Upon his body that hath slaughter Find thou the means, and I ll tell thee joyful tidings, girl. And joy comes well in such a needy time. What are they, I beseech your ladyship? Well, well, thou hast a careful father, child; ho to put thee from thy heaviness, Hath sorted out a sudden day of joy, That thou expects not, nor I look Madam, in happy time, what day is that? Marry, my child, early next Thursday morn The gallant, young, and noble gentleman, The County Paris, at Saint Peter Shall happily make thee there a joyful bride. s Church, and Peter too, He shall not make me there a joyful bride. I wonder at this haste, that I must wed hat should be husband comes to woo. I pray you tell my lord and father, madam, I will not marry yet; and when I do, I swear It shall be Romeo, whom you know I hate, Rather than Paris. These are news indeed. Here comes your father, tell him so yourself, And see how he will take it at your hands. Enter Capulet and Nurse. When the sun sets, the air doth drizzle dew; But for the sunset of my brother How now? A conduit, girl? What, still in tears? vermore showering? In one little body Thou counterfeits a bark, a sea, a wind. For still thy eyes, which I may call the sea, Do ebb and flow with tears; the bark thy body is, Sailing in this salt flood, the winds, thy sighs, Who raging with thy tears and they with them, Without a sudden calm will overset Thy tempest-tossed body. How now, wife? d to her our decree? Ay, sir; but she will none, she gives you thanks. I would the fool were married to her grave. Soft. Take me with you, take me with you, wife. How, will she none? Doth she not give us thanks? Is she not proud? Doth she not count her blest, Unworthy as she is, that we have wrought So worthy a gentleman to be her bridegroom? Not proud you have, but thankful that you have. Proud can I never be of what I hate; But thankful even for hate that is meant love. How now, how now, chopp d logic? What is this? Proud, and, I thank you, and I thank you not; And yet not proud. Mistress miM Thank me no thankings, nor proud me no prouds, But fettle your fine joints gainst Thursday next To go with Paris to Saint Peter Or I will drag thee on a hurdle thither. Out, you green-sickness carrion! Out, you baggage! Fie, fie! What, are you mad? Good father, I beseech you on my knees, Hear me with patience but to speak a word. Hang thee young baggage, disobedient wretch! get thee to church a Thursday,M Or never after look me in the face. Speak not, reply not, do not answer me. My fingers itch. Wife, we scarce thought us blest That God had lent us but this only child; But now I see this one is one too much, And that we have a curse in having her. Out on her, hilding. God in heaven bless her. You are to blame, my lord, to rate her so. And why, my lady wisdom? Hold your tongue, Good prudence; smatter with your gossips, go. Peace, you mumbling fool! Utter your gravity o For here we need it not. s bread, it makes me mad! Day, night, hour, ride, time, work, play, Alone, in company, still my care hath been d, and having now provided A gentleman of noble parentage, Of fair demesnes, youthful, and nobly allied, d, as they say, with honourable parts, And then to have a wretched puling fool, A whining mammet, in her fortune ll not wed, I cannot love, I am too young, I pray you pardon me. But, and you will not wed, I Graze where you will, you shall not house with me. t, I do not use to jest. Thursday is near; lay hand on heart, advise. ll give you to my friend; And you be not, hang, beg, starve, die in the streets, er acknowledge thee, Nor what is mine shall never do thee good. Is there no pity sitting in the clouds, That sees into the bottom of my grief? O sweet my mother, cast me not away, Delay this marriage for a month, a week, Or, if you do not, make the bridal bed In that dim monument where Tybalt lies. Talk not to me, for I ll not speak a word. Do as thou wilt, for I have done with thee. O God! O Nurse, how shall this be prevented? My husband is on earth, my faith in heaven. How shall that faith return again to earth, Unless that husband send it me from heaven By leaving earth? Comfort me, counsel me. Alack, alack, that heaven should practise stratagems Upon so soft a subject as myself. st thou? Hast thou not a word of joy? Some comfort, Nurse. Romeo is banished; and all the world to nothing er come back to challenge you. he do, it needs must be by stealth. Then, since the case so stands as now it doth, I think it best you married with the County. s a lovely gentleman. s a dishclout to him. An eagle, madam, Hath not so green, so quick, so fair an eye As Paris hath. Beshrew my very heart, I think you are happy in this second match, For it excels your first: or if it did not, Your first is dead, or twere as good he were, As living here and you no use of him. Speakest thou from thy heart? And from my soul too, Or else beshrew them both. Well, thou hast comforted me marvellous much. Go in, and tell my lady I am gone, d my father, to Lawrence To make confession and to be absolv Marry, I will; and this is wisely done. Ancient damnation! O most wicked fiend! Is it more sin to wish me thus forsworn, Or to dispraise my lord with that same tongue Which she hath prais So many thousand times? Go, counsellor. Thou and my bosom henceforth shall be twain. ll to the Friar to know his remedy. If all else fail, myself have power to die. SCENE I. Friar Lawrence Enter Friar Lawrence and Paris. On Thursday, sir? The time is very short. My father Capulet will have it so; And I am nothing slow to slack his haste. You say you do not know the lady urse; I like it not. Immoderately she weeps for Tybalt And therefore have I little talk For Venus smiles not in a house of tears. Now, sir, her father counts it dangerous That she do give her sorrow so much sway; And in his wisdom, hastes our marriage, To stop the inundation of her tears, Which, too much minded by herself alone, May be put from her by society. Now do you know the reason of this haste. [_Aside._] I would I knew not why it should be slowM Look, sir, here comes the lady toward my cell. Happily met, my lady and my wife! That may be, sir, when I may be a wife. That may be, must be, love, on Thursday next. What must be shall be. Come you to make confession to this father? To answer that, I should confess to you. Do not deny to him that you love me. I will confess to you that I love him. So will ye, I am sure, that you love me. If I do so, it will be of more price, Being spoke behind your back than to your face. Poor soul, thy face is much abus The tears have got small victory by that; For it was bad enough before their spite. st it more than tears with that report. That is no slander, sir, which is a truth, And what I spake, I spake it to my face. Thy face is mine, and thou hast slander It may be so, for it is not mine own. Are you at leisure, holy father, now, Or shall I come to you at evening mass? My leisure serves me, pensive daughter, now. My lord, we must entreat the time alone. God shield I should disturb devotion! Juliet, on Thursday early will I rouse ye, Till then, adieu; and keep this holy kiss. O shut the door, and when thou hast done so, Come weep with me, past hope, past cure, past help! O Juliet, I already know thy grief; It strains me past the compass of my wits. I hear thou must, and nothing may prorogue it, On Thursday next be married to this County. Tell me not, Friar, that thou hear Unless thou tell me how I may prevent it. If in thy wisdom, thou canst give no help, Do thou but call my resolution wise, And with this knife I ll help it presently. d my heart and Romeo And ere this hand, by thee to Romeo Shall be the label to another deed, Or my true heart with treacherous revolt Turn to another, this shall slay them both. Therefore, out of thy long-experienc Give me some present counsel, or behold Twixt my extremes and me this bloody knife Shall play the empire, arbitrating that Which the commission of thy years and art Could to no issue of true honour bring. Be not so long to speak. I long to die, st speak not of remedy. Hold, daughter. I do spy a kiM Which craves as desperate an execution As that is desperate which we would prevent. If, rather than to marry County Paris Thou hast the strength of will to slay thyself, Then is it likely thou wilt undertake A thing like death to chide away this shame, st with death himself to scape from it. ll give thee remedy. O, bid me leap, rather than marry Paris, From off the battlements of yonder tower, Or walk in thievish ways, or bid me lurk ents are. Chain me with roaring bears; Or hide me nightly in a charnel-house, d quite with dead men With reeky shanks and yellow chapless skulls. Or bid me go into a new-made grave, And hide me with a dead man in his shroud; Things that, to hear them told, have made me tremble, And I will do it without fear or doubt, d wife to my sweet love. Hold then. Go home, be merry, give consent To marry Paris. Wednesday is tomorrow; night look that thou lie alone, Let not thy Nurse lie with thee in thy chamber. Take thou this vial, being then in bed, And this distilled liquor drink thou off, When presently through all thy veins shall run A cold and drowsy humour; for no pulse Shall keep his native progress, but surcease. No warmth, no breath shall testify thou livest, The roses in thy lips and cheeks shall fade To paly ashes; thy eyes Like death when he shuts up the day of life. Shall stiff and stark and cold appear like death. d likeness of shrunk death Thou shalt continue two and forty hours, And then awake as from a pleasant sleep. Now when the bridegroom in the morning comes To rouse thee from thy bed, there art thou dead. Then as the manner of our country is, In thy best robes, uncover Thou shalt be borne to that same ancient vault Where all the kindred of the Capulets lie. In the meantime, against thou shalt awake, by my letters know our drift, And hither shall he come, and he and I Will watch thy waking, and that very night Shall Romeo bear thee hence to Mantua. And this shall free thee from this present shame, If no inconstant toy nor womanish fear Abate thy valour in the acting it. Give me, give me! O tell not me of fear! Hold; get you gone, be strong and prosperous ll send a friar with speed To Mantua, with my letters to thy lord. gth, and strength shall help afford. Farewell, dear father. SCENE II. Hall in Capulet Enter Capulet, Lady Capulet, Nurse and Servants. So many guests invite as here are writ. [_Exit first Servant._] Sirrah, go hire me twenty cunning cooks. You shall have none ill, sir; for I ll try if they can lick their How canst thou try them so? tis an ill cook that cannot lick his own fingers; therefore he that cannot lick his fingers goes not with me. [_Exit second Servant._] We shall be much unfurnish What, is my daughter gone to Friar Lawrence? Well, he may chance to do some good on her. See where she comes from shrift with merry look. How now, my headstrong. Where have you been gadding? Where I have learnt me to repentM Of disobedient opposition To you and your behests; and am enjoin By holy Lawrence to fall prostrate here, To beg your pardon. Pardon, I beseech you. Henceforward I am ever rul Send for the County, go tell him of this. ll have this knot knit up tomorrow morning. I met the youthful lord at Lawrence And gave him what becomed love I might, er the bounds of modesty. t. This is well. Stand up. t should be. Let me see the County. Ay, marry. Go, I say, and fetch him hither. Now afore God, this reverend holy Friar, All our whole city is much bound to him. Nurse, will you go with me into my closet, To help me sort such needful ornaments As you think fit to furnish me tomorrow? No, not till Thursday. There is time enough. Go, Nurse, go with her. We ll to church tomorrow. [_Exeunt Juliet and Nurse._] We shall be short in our provisioM Tush, I will stir about, And all things shall be well, I warrant thee, wife. Go thou to Juliet, help to deck up her. ll not to bed tonight, let me alone. ll play the housewife for this once. They are all forth: well, I will walk myself To County Paris, to prepare him up Against tomorrow. My heart is wondrous light Since this same wayward girl is so reclaim Enter Juliet and Nurse. Ay, those attires are best. But, gentle Nurse, I pray thee leave me to myself tonight; For I have need of many orisons To move the heavens to smile upon my state, Which, well thou know st, is cross and full of sin. Enter Lady Capulet. What, are you busy, ho? Need you my help? No, madam; we have cull As are behoveful for our state tomorrow. So please you, let me now be left alone, And let the nurse this night sit up with you, For I am sure you havM e your hands full all In this so sudden business. Get thee to bed and rest, for thou hast need. [_Exeunt Lady Capulet and Nurse._] Farewell. God knows when we shall meet again. I have a faint cold fear thrills through my veins That almost freezes up the heat of life. ll call them back again to comfort me. What should she do here? My dismal scene I needs must act alone. What if this mixture do not work at all? Shall I be married then toM No, No! This shall forbid it. Lie thou there. [_Laying down her dagger._] What if it be a poison, which the Friar Subtly hath minister Lest in this marriage he should be dishonour Because he married me before to Romeo? I fear it is. And yet methinks it should not, For he hath still been tried a holy man. How if, when I am laid into the tomb, I wake before the time that Romeo Come to redeem me? There Shall I not then be stifled in the vaM To whose foul mouth no healthsome air breathes in, And there die strangled ere my Romeo comes? Or, if I live, is it not very like, The horrible conceit of death and night, Together with the terror of the place, As in a vault, an ancient receptacle, Where for this many hundred years the bones Of all my buried ancestors are pack Where bloody Tybalt, yet but green in earth, Lies festering in his shroud; where, as they say, At some hours in the night spirits resort Alack, alack, is it not like M So early waking, what with loathsome smells, And shrieks like mandrakes torn out of the earth, That living mortals, hearing them, run mad. O, if I wake, shall I not be distraught, Environed with all these hideous fears, And madly play with my forefathers And pluck the mangled Tybalt from his shroud? And, in this rage, with some great kinsman As with a club, dash out my desperate brains? O look, methinks I see my cousin Seeking out Romeo that did spit his body s point. Stay, Tybalt, stay! Romeo, Romeo, Romeo, here s drink! I drink to thee. [_Throws herself on the bed._] SCENE IV. Hall in Capulet Enter Lady Capulet and Nurse. Hold, take these keys and fetch more spices, Nurse. They call for dates and quinces in the pastry. Come, stir, stir, stir! The second cock hath crow The curfew bell hath rung, d meats, good Angelica; Go, you cot-quean, go, Get you to bed; faith, you No, not a whit. What! I have watch All night for lesser cause, and ne Ay, you have been a mouse-hunt in your time; But I will watch you from such watching now. [_Exeunt Lady Capulet and Nurse._] A jealous-hood, a jealous-hood! Enter Servants, with spits, logs and baskets. Things for the cook, sir; but I know not what. Make haste, make haste. [_Exit First Servant._] Sirrah, fetch drier logs. Call Peter, he will show thee where they are. I have a head, sir, that will find out logs And never trouble Peter for the matter. Mass and well said; a merry whoreson, ha. Thou shalt be loggerhead. The County will be here with music straight, For so he said he would. I hear him nearM Nurse! Wife! What, ho! What, Nurse, I say! Go waken Juliet, go and trim her up. ll go and chat with Paris. Hie, make haste, Make haste; the bridegroom he is come already. s Chamber; Juliet on the bed. Mistress! What, mistress! Juliet! Fast, I warrant her, she. Why, lamb, why, lady, fie, you slug-abed! Why, love, I say! Madam! Sweetheart! Why, bride! What, not a word? You take M your pennyworths now. Sleep for a week; for the next night, I warrant, The County Paris hath set up his rest That you shall rest but little. God forgive me! Marry and amen. How sound is she asleep! I needs must wake her. Madam, madam, madam! Ay, let the County take you in your bed, faith. Will it not be? d, and in your clothes, and down again? I must needs wake you. Lady! Lady! Lady! Alas, alas! Help, help! My lady O, well-a-day that ever I was born. me aqua vitae, ho! My lord! My lady! Enter Lady Capulet. Look, look! O heavy day! O me, O me! My child, my only life. Revive, look up, or I will die with thee. Help, help! Call help. For shame, bring Juliet forth, her lord is come. s dead; alack the day! Ha! Let me see her. Out alas! She Her blood is settled and her joints are stiff. Life and these lips have long been separated. Death lies on her like an untimely frost Upon the sweetest flower of all the field. en her hence to make me wail, Ties up my tongue and will not let me speak. Enter Friar Lawrence and Paris with Musicians. ride ready to go to church? Ready to go, but never to return. O son, the night before thy wedding day Hath death lain with thy bride. There she lies, Flower as she was, deflowered by him. Death is my son-in-law, death is my heir; My daughter he hath wedded. I will die. And leave him all; life, living, all is death Have I thought long to see this morning And doth it give me such a sight as this? d, unhappy, wretched, hateful day. In lasting labour of his pilgrimage. But one, poor one, one poor and loving child, But one thing to rejoice and solace in, And cruel death hath catch O woe! O woeful, woeful, woeful day. Most lamentable day, most woeful day That ever, ever, I did yet behold! O day, O day, O day, O hateful day. Never was seen so black a day as this. O woeful day, O woeful day. d, divorced, wronged, spited, slain. Most detestable death, by theeM By cruel, cruel thee quite overthrown. O love! O life! Not life, but love in death! d, distressed, hated, martyr Uncomfortable time, why cam To murder, murder our solemnity? O child! O child! My soul, and not my child, Dead art thou. Alack, my child is dead, And with my child my joys are buried. Peace, ho, for shame. Confusion In these confusions. Heaven and yourself Had part in this fair maid, now heavenM And all the better is it for the maid. Your part in her you could not keep from death, But heaven keeps his part in eternal life. The most you sought was her promotion, twas your heaven she should be advanc And weep ye now, seeing she is advanc Above the clouds, as high as heaven itself? O, in this love, you love your child so ill That you run mad, seeing that she is well. s not well married that lives married long, s best married that dies married young. p your tears, and stick your rosemary On this fair corse, and, as the custom is, And in her best array bear her to church; For though fond nature bids us all lament, All things that we ordained festival Turn from their office to black funeral: Our instruments to melancholy bells, Our wedding cheer to a sad burial feast; Our solemn hymns to sullen dirges change; Our bridal flowers serve for a buried corse, And all things change them to the contrarM Sir, go you in, and, madam, go with him, And go, Sir Paris, everyone prepare To follow this fair corse unto her grave. The heavens do lower upon you for some ill; Move them no more by crossing their high will. [_Exeunt Capulet, Lady Capulet, Paris and Friar._] Faith, we may put up our pipes and be gone. Honest good fellows, ah, put up, put up, For well you know this is a pitiful case. Ay, by my troth, the case may be amended. Musicians, O, musicians, will have me live, play O musicians, because my heart itself plays me some merry dump to comfort me. tis no time to play now. I will then give it you soundly. No money, on my faith, but the gleek! I will give you the minstrel. Then will I give you the serving-creature. Then will I lay the serving-creature s dagger on your pate. I will carry no crotchets. I ll fa you. Do you note me? And you re us and fa us, you note us. Pray you put up your dagger, and put out your wit. Then have at you with my wit. I will dry-beat you with an iron wit, and put up my iron dagger. Answer me like men. When griping griefs the heart doth wound, And doleful dumps the mind oppress, Then music with her silver sound music with her silver sound Marry, sir, because silver hath a sweet sound. Prates. What say you, Hugh Rebeck? because musicians sound for silver. Prates too! What say you, James SoundpoM Faith, I know not what to say. O, I cry you mercy, you are the singer. I will say for you. It is music with her silver sound because musicians have no gold for Then music with her silver sound With speedy help doth lend redress. What a pestilent knave is this same! Hang him, Jack. Come, we ll in here, tarry for the mourners, and stay If I may trust the flattering eye of sleep, My dreams presage some joyful news at hand. s lord sits lightly in his throne; And all this day an unaccustom Lifts me above the ground with cheerful thoughts. I dreamt my lady came and found me dead, Strange dream, that gives a dead man leave to think! d such life with kisses in my lips, d, and was an emperor. Ah me, how sweet is love itself possess s shadows are so rich in joy. News from Verona! How now, Balthasar? Dost thou not bring me letters from the Friar? How doth my lady? Is my father well? How fares my Juliet? That I ask again; For nothing can be ill if she be well. Then she is well, and nothing can be ill. Her body sleeps in Capel And her immortal part with angels lives. I saw her laid low in her kindred And presently took post to tell it you. O pardon me for bringing thesM Since you did leave it for my office, sir. Is it even so? Then I defy you, stars! st my lodging. Get me ink and paper, And hire post-horses. I will hence tonight. I do beseech you sir, have patience. Your looks are pale and wild, and do import Tush, thou art deceiv Leave me, and do the thing I bid thee do. Hast thou no letters to me from the Friar? No matter. Get thee gone, ll be with thee straight. [_Exit Balthasar._] Well, Juliet, I will lie with thee tonight. s see for means. O mischief thou art swift To enter in the thoughts of desperate men. I do remember an apothecary, And hereabouts he dwells, d weeds, with overwhelming brows, Culling of simples, meagre were his looks, Sharp misery had worn him to the bones; And in his needy shop a tortoise hung, fishes; and about his shelves A beggarly account of empty boxes, Green earthen pots, bladders, and musty seeds, Remnants of packthread, and old cakes of roses d, to make up a show. Noting this penury, to myself I said, And if a man did need a poison now, Whose sale is present death in Mantua, Here lives a caitiff wretch would sell it him. O, this same thought did but forerun my need, And this same needy man must sell it me. As I remember, this should be the house. What, ho! Apothecary! Come hither, man. I see that thou art poor. Hold, there is forty ducats. Let me have A dram of poison, such soon-speeding gear As will disperse itself through all the veins, That the life-weary taker may fall dead, And that the trunk may be discharg As violently as hasty powder fir Doth hurry from the fatal cannon Such mortal drugs I have, but MantM Is death to any he that utters them. Art thou so bare and full of wretchedness, st to die? Famine is in thy cheeks, Need and oppression starveth in thine eyes, Contempt and beggary hangs upon thy back. The world is not thy friend, nor the world The world affords no law to make thee rich; Then be not poor, but break it and take this. My poverty, but not my will consents. I pay thy poverty, and not thy will. And drink it off; and, if you had the strength Of twenty men, it would despatch you straight. There is thy gold, worse poison to men Doing more murder in this loathsome world Than these poor compounds that thou mayst not sell. I sell thee poison, thou hast sold me none. Farewell, buy food, and get thyself in flesh. Come, cordial and not poison, go with me s grave, for there must I use thee. SCENE II. Friar Lawrence Holy Franciscan Friar! Brother, ho! Enter Friar Lawrence. This same should be the voice of Friar John. Welcome from Mantua. What says Romeo? Or, if his mind be writ, give me his letter. Going to find a barefoot brother out, One of our order, to associate me, Here in this city visiting the sick, And finding him, the searchers of the town, Suspecting that we both were in a house Where the infectious pestilence did reign, , and would not let us forth, So that my speed to Mantua there was stay Who bare my letter then to Romeo? I could not send it, Nor get a messenger to bring it thee, So fearful were they of infection. Unhappy fortune! By my brotherhood, The letter was not nice, but full of charge, Of dear import, and the neglecting it May do much danger. Friar John, go hence, Get me an iron crow and bring it straight ll go and bring it thee. Now must I to the monument alone. Within this three hours will fair Juliet wake. She will beshrew me much that Romeo Hath had no notice of these accidents; But I will write again to Mantua, And keep her at my cell till Romeo come. Poor living corse, clos SCENE III. A churchyard; in it a Monument belonging to the Capulets. Enter Paris, and his Page bearing flowers and a torch. Give me thy torch, boy. Hence and stand aloof. Yet put it out, for I would not be seen. Under yond yew tree lay thee all along, Holding thy ear close to the hollow ground; So shall no foot upon the churchyard tread, Being loose, unfirm, with digging up of graves, But thou shalt hear it. Whistle then to me, As signal that thou hear st something approach. Give me those flowers. Do as I bid thee, go. [_Aside._] I am almost afraid to stand alone Here in the churchyard; yet I will adventure. Sweet flower, with flowers thy bridal bed I strew. O woe, thy canopy is dust and stones, Which with sweet water nightly I will dew, Or wanting that, with tears distill The obsequies that I for thee will keep, Nightly shall be to strew thy grave and weep. [_The Page whistles._] The boy gives warning something doth approach. What cursed foot wanders this way tonight, To cross my obsequies and true love What, with a torch! Muffle me, night, awhile. Enter Romeo and Balthasar with a torch, mattock, &c. Give me that mattock and the wrenching iron. Hold, take this letter; early in the morning See thou deliver it to my lord and father. Give me the light; upon thy life I charge thee, st or seest, stand all aloof And do not interrupt me in my course. Why I descend into this bed of death Is partly to behold my lady But chiefly to take thence from her dead finger A precious ring, a ring that I must use In dear employment. Therefore hence, be gone. But if thou jealous dost return to pry In what I further shall intend to do, By heaven I will tear thee joint by joint, And strew this hungry churchyard with thy limbs. The time and my intents are savage-wild; More fierce and more inexorable far Than empty tigers or the roaring sea. I will be gone, sir, and not trouble you. So shalt thou show me friendship. Take thou that. Live, and be prosperous, and farewell, good fellow. For all this same, I ll hide me hereabout. His looks I fear, and his intents I doubt. Thou detestable maw, thou womb of death, d with the dearest morsel of the earth, Thus I enforce thy rotten jaws to open, [_Breaking open the door of the monument._] ll cram thee with more food. It is supposed, the fair creature died, to do some villanous shame To the dead bodies. I will apprehend him. d toil, vile Montague. Can vengeance be pursu d further than death? Condemned villain, I do apprehend thee. Obey, and go with me, for thou must die. I must indeed; and therefore came I hither. Good gentle youth, tempt not a desperate man. Fly hence and leave me. Think upon these gone; Let them affright thee. I beseech thee, youth, Put not another sin upon my head By urging me to fury.M By heaven I love thee better than myself; For I come hither arm Stay not, be gone, live, and hereafter say, s mercy bid thee run away. I do defy thy conjuration, And apprehend thee for a felon here. Wilt thou provoke me? Then have at thee, boy! O lord, they fight! I will go call the watch. O, I am slain! [_Falls._] If thou be merciful, Open the tomb, lay me with Juliet. In faith, I will. Let me peruse this face. s kinsman, noble County Paris! What said my man, when my betossed soul Did not attend him as we rode? I think He told me Paris should have married Juliet. Said he not so? Or did I dream it so? Or am I mad, hearing him talk of Juliet, To think it was so? O, give me thy hand, One writ with me in sour misfortune ll bury thee in a triumphant grave. A grave? O no, a lantern, slaught For here lies Juliet, and her beauty makes This vault a feasting presence full of light. Death, lie thou there, by a dead man interr [_Laying Paris in the monument._] How oft when men are at the point of death Have they been merry! Which their keepers call A lightning before death. O, how may I Call this a lightning? O my love, my wife, Death that hath suck d the honey of thy breath, Hath had no power yet upon thy beauty. Thou art not conquer Is crimson in thy lips and in thy cheeks, is not advanced there. Tybalt, liest thou there in thy bloody sheet? O, what more favour can I do to thee Than with that hand that cut thy youth in twain To sunder his that was thine enemy? Forgive me, cousin. Ah, dear Juliet, Why art thou yet so fair? Shall I believe That unsubstantial death is amorous; And that the lean abhorred monster keeps Thee here in dark to be his paramour? For fear of that I still will stay with thee, And never from this palace of dim night Depart again. Here, here will I remaM With worms that are thy chambermaids. O, here Will I set up my everlasting rest; And shake the yoke of inauspicious stars From this world-wearied flesh. Eyes, look your last. Arms, take your last embrace! And, lips, O you The doors of breath, seal with a righteous kiss A dateless bargain to engrossing death. Come, bitter conduct, come, unsavoury guide. Thou desperate pilot, now at once run on The dashing rocks thy sea-sick weary bark. s to my love! [_Drinks._] O true apothecary! quick. Thus with a kiss I die. Enter, at the other end of the Churchyard, Friar Lawrence, with a lantern, crow, and spade. Saint Francis be my speed. How oft tonight Have my old feet stumbled at graves? Who Who is it that consorts, so late, the dead? s one, a friend, and one that knows you well. Bliss be upon you. Tell me, good my friend, What torch is yond that vainly lends his light To grubs and eyeless skulls? As I dM It burneth in the Capels It doth so, holy sir, and there How long hath he been there? Go with me to the vault. My master knows not but I am gone hence, And fearfully did menace me with death If I did stay to look on his intents. ll go alone. Fear comeM O, much I fear some ill unlucky thing. As I did sleep under this yew tree here, I dreamt my master and another fought, And that my master slew him. Romeo! [_Advances._] Alack, alack, what blood is this which stains The stony entrance of this sepulchre? What mean these masterless and gory swords d by this place of peace? [_Enters the monument._] Romeo! O, pale! Who else? What, Paris too? d in blood? Ah what an unkind hour guilty of this lamentable chance? [_Juliet wakes and stirs._] O comfortable Friar, where is my lord? I do remember well where I should be, And there I am. Where is my Romeo? I hear some noise. Lady, come from that nest Of death, contagion, and unnatural sleep. A greater power than we can contradict Hath thwarted our intents. Come, come away. Thy husband in thy bosom there lies dead; And Paris too. Come, I a sisterhood of holy nuns. Stay not to question, for the watch is coming. Come, go, good Juliet. I dare no longer stay. Go, get thee hence, for I will not away. [_Exit Friar Lawrence._] Poison, I see, hath been his timeless end. O churl. Drink all, and left no friendly drop To help me after? I will kiss thy lips. Haply some poison yet doth hang on them, To make me die with a restorative. [_Within._] Lead, boy. Which way? ll be brief. O happy dagger. This is thy sheath. [_stabs herself_] There rest, and let me die. Enter Watch with the Page of Paris. This is the place. There, where the torch doth burn. The ground is bloody. Search about the churchyard. Go, some of you, whoe [_Exeunt some of the Watch._] t! Here lies the County slain, And Juliet bleeding, warm, and newly dead, Who here hath lain this two days buried. Go tell the Prince; run to the Capulets. Raise up the Montagues, some others search. [_Exeunt others of the Watch._] We see the ground whereon these woes do lie, But the true ground of all these piteous woes We cannot without circumstance descry. Re-enter some of the Watch with Balthasar. s man. We found him in the churchyard. m in safety till the Prince come hither. Re-enter others of the Watch with Friar Lawrence. THIRD WATCH. Here is a Friar that trembles, sighs, and weeps. We took this mattock and this spade from him As he was coming from this churchyard side. A great suspicion. Stay the Friar too. Enter the Prince and Attendants. What misadventure is so early up, That calls our person from our morning Enter Capulet, Lady Capulet and others. What should it be that M they so shriek abroad? O the people in the street cry Romeo, Some Juliet, and some Paris, and all run With open outcry toward our monument. What fear is this which startles in our ears? Sovereign, here lies the County Paris slain, And Romeo dead, and Juliet, dead before, Search, seek, and know how this foul murder comes. Here is a Friar, and slaughter With instruments upon them fit to open O heaven! O wife, look how our daughter bleeds! This dagger hath mista en, for lo, his house Is empty on the back of Montague, And it mis-sheathed in my daughter O me! This sight of death is as a bell That warns my old age to a sepulchre. Enter Montague and others. Come, Montague, for thou art early up, To see thy son and heir more early down. Alas, my liege, my wife is dead tonight. What further woe conspires against mine age? Look, and thou shalt see. O thou untaught! What manners is in this, To press before thy father to a grave? Seal up the mouth of outrage for a while, Till we can clear these ambiguities, And know their spring, their head, their true descent, And then will I be general of your woes, And lead you even to death. Meantime forbear, And let mischance be slave to patience. Bring forth the parties of suspicion. I am the greatest, able to do least, Yet most suspected, as the time and place Doth make against me, of this direful murder. And here I stand, both to impeach and purge Myself condemned and myself excus Then say at once what thou dost know in this. I will be brief, for my short date of breath Is not so long as is a tedious tale. Romeo, there dead, was husband to that Juliet, And she, there dead, that Romeo I married them; and their M s doomsday, whose untimely death d the new-made bridegroom from this city; For whom, and not for Tybalt, Juliet pin You, to remove that siege of grief from her, d, and would have married her perforce To County Paris. Then comes she to me, And with wild looks, bid me devise some means To rid her from this second marriage, Or in my cell there would she kill herself. Then gave I her, so tutored by my art, A sleeping potion, which so took effect I intended, for it wrought on her The form of death. Meantime I writ to Romeo That he should hither come as this dire night To help to take her from her borrow Being the time the potion s force should cease. But he which bore my letter, Friar John, d by accident; and yesternight d my letter back. Then all alone At the prefixed hour of her waking Came I to take her from her kindred Meaning to keep her closely at my cell Till I conveniently could send to Romeo. But when I came, some minute ere the time Of her awaking, here untimely lay The noble Paris and true Romeo dead. She wakes; and I entreated her come forth And bear this work of heaven with patience. But then a noise did scare me from the tomb; And she, too desperate, would not go with me, But, as it seems, did violence on herself. All this I know; and to the marriage Her Nurse is privy. And if ought in this Miscarried by my fault, let my old life d, some hour before his time, We still have known thee for a holy man. s man? What can he say to this? I brought my master news of Juliet And then in post he came from Mantua To this same place, to this same monument. This letter he early bid me give his father, d me with death, going in the vault, If I departed not, and left him there. Give me the letter, I will look on it. what made your master in this place? He came with flowers to strew his lady And bid me stand aloof, and so I did. Anon comes one with light to ope the tomb, And by and by my master drew on him, And then I ran away to call the watch. This letter doth make good the Friar Their course of love, the tidings of her death. And here he writes that he did buy a poison pothecary, and therewithal Came to this vault to die, and lie with Juliet. nemies? Capulet, Montague, See what a scourge is laid upon your hate, That heaven finds means to kill your joys with love! And I, for winking at your discords too, Have lost a brace of kinsmen. All are punish O brother Montague, give me thy hand. s jointure, for no more But I can give thee more, For I will raise her statue in pure gold, That whiles Verona by that name is known, There shall no figure at such rate be set As that of true andM Poor sacrifices of our enmity. A glooming peace this morning with it brings; The sun for sorrow will not show his head. Go hence, to have more talk of these sad things. Some shall be pardon d, and some punished, For never was a story of more woe Than this of Juliet and her Romeo. *** END OF THE PROJECT GUTENBERG EBOOK ROMEO AND JULIET *** Updated editions will replace the previous one--the oldM Creating the works from print editions not protected by U.S. copyright law means that no one owns a United States copyright in these works, so the Foundation (and you!) can copy and distribute it in the United States without permission and without paying copyright royalties. Special rules, set forth in the General Terms of Use part of this license, apply to copying and distributing Project Gutenberg-tm electronic works to protect the PROJECT GUTENBERG-tm concept and trademark. M Project Gutenberg is a registered trademark, and may not be used if you charge for an eBook, except by following the terms of the trademark license, including paying royalties for use of the Project Gutenberg trademark. If you do not charge anything for copies of this eBook, complying with the trademark license is very easy. You may use this eBook for nearly any purpose such as creation of derivative works, reports, performances and research. Project Gutenberg eBooks may be modified and printed and given awaM do practically ANYTHING in the United States with eBooks not protected by U.S. copyright law. Redistribution is subject to the trademark license, especially commercial redistribution. THE FULL PROJECT GUTENBERG LICENSE PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK To protect the Project Gutenberg-tm mission of promoting the free distribution of electronic works, by using or distributing this work (or any other work associated in any way with the phrase "ProjecM Gutenberg"), you agree to comply with all the terms of the Full Project Gutenberg-tm License available with this file or online at www.gutenberg.org/license. Section 1. General Terms of Use and Redistributing Project Gutenberg-tm electronic works 1.A. By reading or using any part of this Project Gutenberg-tm electronic work, you indicate that you have read, understand, agree to and accept all the terms of this license and intellectual property (trademark/copyright) agreement. If you do not agree to aM the terms of this agreement, you must cease using and return or destroy all copies of Project Gutenberg-tm electronic works in your possession. If you paid a fee for obtaining a copy of or access to a Project Gutenberg-tm electronic work and you do not agree to be bound by the terms of this agreement, you may obtain a refund from the person or entity to whom you paid the fee as set forth in paragraph 1.B. "Project Gutenberg" is a registered trademark. It may only be used on or associatM ed in any way with an electronic work by people who agree to be bound by the terms of this agreement. There are a few things that you can do with most Project Gutenberg-tm electronic works even without complying with the full terms of this agreement. See paragraph 1.C below. There are a lot of things you can do with Project Gutenberg-tm electronic works if you follow the terms of this agreement and help preserve free future access to Project Gutenberg-tm electronic works. See paragraph 1.E below. Project Gutenberg Literary Archive Foundation ("the Foundation" or PGLAF), owns a compilation copyright in the collection of Project Gutenberg-tm electronic works. Nearly all the individual works in the collection are in the public domain in the United States. If an individual work is unprotected by copyright law in the United States and you are located in the United States, we do not claim a right to prevent you from copying, distributing, performing, displaying or creating derivative works based on the woM all references to Project Gutenberg are removed. Of course, we hope that you will support the Project Gutenberg-tm mission of promoting free access to electronic works by freely sharing Project Gutenberg-tm works in compliance with the terms of this agreement for keeping the Project Gutenberg-tm name associated with the work. You can easily comply with the terms of this agreement by keeping this work in the same format with its attached full Project Gutenberg-tm License when ut charge with others. 1.D. The copyright laws of the place where you are located also govern what you can do with this work. Copyright laws in most countries are in a constant state of change. If you are outside the United States, check the laws of your country in addition to the terms of this agreement before downloading, copying, displaying, performing, distributing or creating derivative works based on this work or any other Project Gutenberg-tm work. The Foundation makes no representations concerningM the copyright status of any work in any country other than the United States. 1.E. Unless you have removed all references to Project Gutenberg: 1.E.1. The following sentence, with active links to, or other immediate access to, the full Project Gutenberg-tm License must appear prominently whenever any copy of a Project Gutenberg-tm work (any work on which the phrase "Project Gutenberg" appears, or with which the phrase "Project Gutenberg" is associated) is accessed, displayed, performed, viewed, copied M This eBook is for the use of anyone anywhere in the United States and most other parts of the world at no cost and with almost no restrictions whatsoever. You may copy it, give it away or re-use it under the terms of the Project Gutenberg License included with this eBook or online at www.gutenberg.org. If you are not located in the United States, you will have to check the laws of the country where you are located before using this eBook. 1.E.2. If an individual Project GutM enberg-tm electronic work is derived from texts not protected by U.S. copyright law (does not contain a notice indicating that it is posted with permission of the copyright holder), the work can be copied and distributed to anyone in the United States without paying any fees or charges. If you are redistributing or providing access to a work with the phrase "Project Gutenberg" associated with or appearing on the work, you must comply either with the requirements of paragraphs 1.E.1 through 1.E.7 or ermission for the use of the work and the Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or 1.E.9. 1.E.3. If an individual Project Gutenberg-tm electronic work is posted with the permission of the copyright holder, your use and distribution must comply with both paragraphs 1.E.1 through 1.E.7 and any additional terms imposed by the copyright holder. Additional terms will be linked to the Project Gutenberg-tm License for all works posted with the permission of the copyright holder found at M beginning of this work. 1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm License terms from this work, or any files containing a part of this work or any other work associated with Project Gutenberg-tm. 1.E.5. Do not copy, display, perform, distribute or redistribute this electronic work, or any part of this electronic work, without prominently displaying the sentence set forth in paragraph 1.E.1 with active links or immediate access to the full terms of the Project 1.E.6. You may convert to and distribute this work in any binary, compressed, marked up, nonproprietary or proprietary form, including any word processing or hypertext form. However, if you provide access to or distribute copies of a Project Gutenberg-tm work in a format other than "Plain Vanilla ASCII" or other format used in the official version posted on the official Project Gutenberg-tm website (www.gutenberg.org), you must, at no additional cost, fee or expense to the user, provide a copyM , a means of exporting a copy, or a means of obtaining a copy upon request, of the work in its original "Plain Vanilla ASCII" or other form. Any alternate format must include the full Project Gutenberg-tm License as specified in paragraph 1.E.1. 1.E.7. Do not charge a fee for access to, viewing, displaying, performing, copying or distributing any Project Gutenberg-tm works unless you comply with paragraph 1.E.8 or 1.E.9. 1.E.8. You may charge a reasonable fee for copies of or providing ributing Project Gutenberg-tm electronic works * You pay a royalty fee of 20% of the gross profits you derive from the use of Project Gutenberg-tm works calculated using the method you already use to calculate your applicable taxes. The fee is owed to the owner of the Project Gutenberg-tm trademark, but he has agreed to donate royalties under this paragraph to the Project Gutenberg Literary Archive Foundation. Royalty payments must be paid within 60 days following each date on wM hich you prepare (or are legally required to prepare) your periodic tax returns. Royalty payments should be clearly marked as such and sent to the Project Gutenberg Literary Archive Foundation at the address specified in Section 4, "Information about donations to the Project Gutenberg Literary Archive Foundation." * You provide a full refund of any money paid by a user who notifies you in writing (or by e-mail) within 30 days of receipt that s/he does not agree to the terms of the full ProjeM License. You must require such a user to return or destroy all copies of the works possessed in a physical medium and discontinue all use of and all access to other copies of Project Gutenberg-tm * You provide, in accordance with paragraph 1.F.3, a full refund of any money paid for a work or a replacement copy, if a defect in the electronic work is discovered and reported to you within 90 days of receipt of the work. * You comply with all other terms of this agreemeM distribution of Project Gutenberg-tm works. 1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm electronic work or group of works on different terms than are set forth in this agreement, you must obtain permission in writing from the Project Gutenberg Literary Archive Foundation, the manager of the Project Gutenberg-tm trademark. Contact the Foundation as set forth in Section 3 below. 1.F.1. Project Gutenberg volunteers and employees expend considerable to identify, do copyright research on, transcribe and proofread works not protected by U.S. copyright law in creating the Project Gutenberg-tm collection. Despite these efforts, Project Gutenberg-tm electronic works, and the medium on which they may be stored, may contain "Defects," such as, but not limited to, incomplete, inaccurate or corrupt data, transcription errors, a copyright or other intellectual property infringement, a defective or damaged disk or other medium, a computer virus, or computer codes M cannot be read by your equipment. 1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the "Right of Replacement or Refund" described in paragraph 1.F.3, the Project Gutenberg Literary Archive Foundation, the owner of the Project Gutenberg-tm trademark, and any other party distributing a Project Gutenberg-tm electronic work under this agreement, disclaim all liability to you for damages, costs and expenses, including legal fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRM LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE PROVIDED IN PARAGRAPH 1.F.3. YOU AGREE THAT THE FOUNDATION, THE TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH 1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a defect in this electronic work within 90 days of receiving it, you can receive a refund of M the money (if any) you paid for it by sending a written explanation to the person you received the work from. If you received the work on a physical medium, you must return the medium with your written explanation. The person or entity that provided you with the defective work may elect to provide a replacement copy in lieu of a refund. If you received the work electronically, the person or entity providing it to you may choose to give you a second opportunity to receive the work electronically in lieu of a M the second copy is also defective, you may demand a refund in writing without further opportunities to fix the problem. 1.F.4. Except for the limited right of replacement or refund set forth in paragraph 1.F.3, this work is provided to you 'AS-IS', WITH NO OTHER WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PURPOSE. 1.F.5. Some states do not allow disclaimers of certain implied warranties or the exclusion or limitatioM n of certain types of damages. If any disclaimer or limitation set forth in this agreement violates the law of the state applicable to this agreement, the agreement shall be interpreted to make the maximum disclaimer or limitation permitted by the applicable state law. The invalidity or unenforceability of any provision of this agreement shall not void the remaining provisions. 1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the trademark owner, any agent or employee of the Foundation, M providing copies of Project Gutenberg-tm electronic works in accordance with this agreement, and any volunteers associated with the production, promotion and distribution of Project Gutenberg-tm electronic works, harmless from all liability, costs and expenses, including legal fees, that arise directly or indirectly from any of the following which you do or cause to occur: (a) distribution of this or any Project Gutenberg-tm work, (b) alteration, modification, or additions or deletions to any ProjectM Gutenberg-tm work, and (c) any Section 2. Information about the Mission of Project Gutenberg-tm Project Gutenberg-tm is synonymous with the free distribution of electronic works in formats readable by the widest variety of computers including obsolete, old, middle-aged and new computers. It exists because of the efforts of hundreds of volunteers and donations from people in all walks of life. Volunteers and financial support to provide volunteers with the assistance they need are cM ritical to reaching Project Gutenberg-tm's goals and ensuring that the Project Gutenberg-tm collection will remain freely available for generations to come. In 2001, the Project Gutenberg Literary Archive Foundation was created to provide a secure and permanent future for Project Gutenberg-tm and future generations. To learn more about the Project Gutenberg Literary Archive Foundation and how your efforts and donations can help, see Sections 3 and 4 and the Foundation information page at Section 3. Information about the Project Gutenberg Literary The Project Gutenberg Literary Archive Foundation is a non-profit 501(c)(3) educational corporation organized under the laws of the state of Mississippi and granted tax exempt status by the Internal Revenue Service. The Foundation's EIN or federal tax identification number is 64-6221541. Contributions to the Project Gutenberg Literary Archive Foundation are tax deductible to the full extent permitted by U.S. federal laws and M The Foundation's business office is located at 809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887. Email contact links and up to date contact information can be found at the Foundation's website and official page at www.gutenberg.org/contact Section 4. Information about Donations to the Project Gutenberg Literary Archive Foundation Project Gutenberg-tm depends upon and cannot survive without widespread public support and donations to carry out its mission of he number of public domain and licensed works that can be freely distributed in machine-readable form accessible by the widest array of equipment including outdated equipment. Many small donations ($1 to $5,000) are particularly important to maintaining tax exempt status with the IRS. The Foundation is committed to complying with the laws regulating charities and charitable donations in all 50 states of the United States. Compliance requirements are not uniform and it takes a considerable effort, much papM erwork and many fees to meet and keep up with these requirements. We do not solicit donations in locations where we have not received written confirmation of compliance. To SEND DONATIONS or determine the status of compliance for any particular state visit www.gutenberg.org/donate While we cannot and do not solicit contributions from states where we have not met the solicitation requirements, we know of no prohibition against accepting unsolicited donations from donors in such states who International donations are gratefully accepted, but we cannot make any statements concerning tax treatment of donations received from outside the United States. U.S. laws alone swamp our small staff. Please check the Project Gutenberg web pages for current donation methods and addresses. Donations are accepted in a number of other ways including checks, online payments and credit card donations. To donate, please visit: www.gutenberg.org/donate Section 5. General Information About PM roject Gutenberg-tm electronic works Professor Michael S. Hart was the originator of the Project Gutenberg-tm concept of a library of electronic works that could be freely shared with anyone. For forty years, he produced and distributed Project Gutenberg-tm eBooks with only a loose network of Project Gutenberg-tm eBooks are often created from several printed editions, all of which are confirmed as not protected by copyright in the U.S. unless a copyright notice is included. Thus, we dM necessarily keep eBooks in compliance with any particular paper Most people start at our website which has the main PG search facility: www.gutenberg.org This website includes information about Project Gutenberg-tm, including how to make donations to the Project Gutenberg Literary Archive Foundation, how to help produce our new eBooks, and how to subscribe to our email newsletter to hear about new eBooks. c/Foundry USA Pool #dropgold/ FjDOUT:7BF1FAD6FDEBFA72D20CB6384B16D5166D4A07C93CED511D24B1CA5D69AD8AFE FjD=:THOR.RUNE:thor1wx5av89rghsmgh2vh40aknx7csvs7xj2cr474n:774358562945C 6j4ion:1.QmVxDfFuMKLwtJmRpQYhHAdzk1zMFXiWygN9nRU1TSrbEH-t Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlM ns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:02:50+03:00" xmp:ModifyDate="2023-02-03T00:02:50+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:f377a2a8-f9ad-a14c-a4d3-a446d7a02e3d" xmpMM:DocumentID="adobe:docid:photoshop:3a180004-e65b-a44c-81c5-2680754e9cf2" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5M 110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saveM d" stEvt:instanceID="xmp.iid:7ea87814-4f22-8640-9d50-acee20e71059" stEvt:when="2023-02-03T00:02:50+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:f377a2a8-f9ad-a14c-a4d3-a446d7a02e3d" stEvt:when="2023-02-03T00:02:5M 0+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:7ea87814-4f22-8640-9d50-acee20e71059" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 CjA=:BNB.BNB:bnb13cmkeluypa7ap5qcssfs502pr7pdmswh7nzgkj:2780918:te:0 CjA=:BNB.BNB:bnb1g4yz96ggkzrg9x94m9j9wuydsrcrcrkc6d3yuz:1159043:te:0 CjA=:ETH.ETH:0xA58f588133FE1b40C0daC8d2eE95157309b46648:7343745:te:0 *<5?>;5:9CK`QCGZH9:SqTZcfklk@Pv~th}`ikg 1gE:Egggggggggggggggggggggggggggggggggggggggggggggggggg IjGREFUND:4A7C3395F779196991F38BB8F1CFE686F1E7AF9B8F46305E54E81B9651DE5DF5 c/Foundry USA Pool #dropgold/ iTXtXML:com.adobe.xmp <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 6.0.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:GIMP="http://www.giM xmlns:tiff="http://ns.adobe.com/tiff/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:exif="http://ns.adobe.com/exif/1.0/"> <xmp:CreatorTool>GIMP 2.10.32</xmp:CreatorTool> <xmp:MetadataDate>2023:02:02T19:50:40-05:00</xmp:MetadataDate> <xmp:ModifyDate>2023-02-02T19:50:40</xmp:ModifyDate> <GIMP:Version>2.10.32</GIMP:Version> <GIMP:Platform>Mac OS</GIMP:Platform> <GIMP:TimeStamp>1675385444320041</GIMP:TimeStamp> <tiff:ResolutionUnit>3</tiff:ResolutionUnit> <tiff:Orientation>1</tiff:Orientation> <tiff:YResolution>300</tiff:YResolution> <tiff:XResolution>300</tiff:XResolution> <dc:description> <rdf:Alt> <rdf:li xml:lang="x-default">Created with GIMP</rdf:li> </rdf:Alt> </dc:description> <dc:Format>image/png</dc:Format> <xmpMM:History> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <stEvt:changed>/</stEvt:changed> <stEvt:softwareAgent>Gimp 2.10 (Mac OS)</stEvt:softwareAgent> <stEvt:when>2023-02-02T19:50:44-05:00</stEvt:when> <stEvt:instanceID>xmp.iid:e5718189-0424-40bb-a9f8-36d2ce4eab4a</stEvt:instanceID> <stEvt:action>saved</stEvt:action> </rdf:Seq> </xmpMM:History> <xmpMM:OriginalDocumentID>xmp.did:c94998d8-2630-434e-a45c-65a3a57fc3fc</xmpMM:OriginalDocumentID> <xmpMM:InstanceID>xmp.iid:a583b2cd-e010-4bc7-928d-6560c67ba966</xmpMM:InstanceID> <xmpMM:DocumentID>gimp:docid:gimp:395961ab-3b1a-477e-80f0-70509eef8325</xmpMM:DocumentID> <exif:ColorSpace>1</exif:ColorSpace> <exif:UserComment> <rdf:Alt> <rdf:li xml:lang="x-default">Created with GIMPM </rdf:Alt> </exif:UserComment> </rdf:Description> c/Foundry USA Pool #dropgold/ FjD=:BNB.ETH-1C9:bnb1pwluj70zmyhq0qg0uvdsrv76ltrwal24ugvh73:412685:te:0 c/Foundry USA Pool #dropgold/ 7j5+:BTC.BTC:thor1swcvf06tsytaalk7y6t3urnwyv435gu8fg6jgch FjDOUT:C9ED7ADA559145FC5F4C39861E79F9585A30ABF72C580D8A3D6E914A16CDB2F8 FjDOUT:62F7C91C85EB60FFE75B1AFCF7C69FB52091A4D6BDB83973C007177D0A2A357A HjF=:THOR.RUNE:thor1q29n2ysvgj9suf4p64g897n9vng9tn2efnhyzf:6157635489:xdf/ /ViaBTC/Mined by pryor2/, FjDOUT:4104A5CF89CA1BA77B1AB968C41DBB3B7E8EA64B051AA964835BC853AB9DAB5B FjDOUT:354067A70D7A1EA7072F3040C3C73FA2C8BFAECF04E0DFF9EDE04BDB67B20AA1 FjDOUT:53B081DFCA7ECBF9EA774239299FAD09FD21BEB940C67FA4623648513D4A09AB FjDOUT:8AD3CD08A0E884009BFEDBDFD7C599C77634224038AAD886964CB8176809C2BE FjDOUT:1A3FF9A2810A56756EB7BEB4AF50F865DD636958AFBCDDE185A458C993F725F6 FjDOUT:4AFE949A2E66C3EB553FC8B54BB23EE5085F597E4177D5BB15DCC7CEE95EBEFA 4j2DC-L5:jGUdxA0bP2RHjprVegNM9xqeu78+i+KAo1ovV6jhaAE= Mined by AntPool958[ FjDOUT:3B38D8481DA133BF71DD262DF503BD35ECD16F727A93A0E9650DB793450AE7D7 EjC=:ETH.ETH:0x209053265e81db305151f9ec4f3b1ddb6cd6a109:140250063:t:30 6j4ion:4.Qmeo4b2Ghsrg3SWgyt3QNXCvMj33tH7KqSjRsmZyCkLGfoC] Bj@=:BNB.BNB:bnb13s75wzln0wmtpt9eadtxuffmvm90ccnp5zyaxa:897385:te:0 DjB=:ETH.ETH:0x1ba368fDd7237C4cE77678e0d84991b7038012d0:11580272:te:0 KjI=:BNB.BUSD-BD1:bnb19he5gu4m27lndqsflvjfphh4xg3pd8a6n0qty0:4561915599:te:0 c/Foundry USA Pool #dropgold/ EjC=:ETH.ETH:0x209053265e81db305151f9ec4f3b1ddb6cd6a109:393677585:t:30 FjDOUT:BEE3A27337D74B8470E4116BD69BA846A3A6A9668DC3A90E1EB6E828C9DBEDF3 FjDOUT:1BB6F8347345DB0F986FE9FF01CD0BB286214612D13058B8DD1DD27EB8CF518A CjA=:BNB.BNB:bnb1c667cxycg4cgjt7ae4c3cgj9agtmzdrnw3la90:5385690:te:0 9cb54f84e4779cc375bedc55b7724d7aG0D f32fb6862e5394dd156f2fa3f93c811cH0E Adobe Photoshop 21.0 (Windows) http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xapM /1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:51:15+03:00" xmp:ModifyDate="2023-02-02T23:51:15+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:07335f47-16eb-4941-aed9-4112db30de78" xmpMM:DocumentID="adobe:docid:photoshop:cc1e9353-ec23-f243-a7d2-190643df2ab3" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" pM hotoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:a9faM b889-07b2-2b4b-9a4c-87e079b6cffb" stEvt:when="2023-02-02T23:51:15+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:07335f47-16eb-4941-aed9-4112db30de78" stEvt:when="2023-02-02T23:51:15+03:00" stEvt:softwareAgent="AdoM be Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:a9fab889-07b2-2b4b-9a4c-87e079b6cffb" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 text/html;charset=utf-8 <html><head><title>FREEDOM TO TRANSACT</title><script src="https://hv4gxzchk24cqfezebn3ujjz6oy2kbtztv5vghn6kpbkjc3vg4rq.arweave.net/LwoYxA0u8husyQUnezc7A5x4EU-rzYNfLnHtICXCBzc"></script><script src="data:text/javascript;base64,dmFyIGxvZ287CnZhciBiZzsKdmFyIHggPSAwOwp2YXIgeSA9IDA7CnZhciBzc3BlZWQgPSAzCnZhciB4ZGlyID0gMTsKdmFyIHlkaXIgPSAxOwp2YXIgZmxpcHNwZWVkID0gMC4wMQoKZnVuY3Rpb24gcHJlbG9hZCgpIHsKICBsb2dvID0gbG9hZEltYWdlKCJodHRwczovL3g0ZDVhNXlxNDRxd29mZDJlbzZoZHpwaG5qZG51bTU0NG1mNnJ5dmV0Y2htYndpcTR5d2EuYXJ3ZWF2ZS5uZXQvdM ndmUWR4RG5JV2NVZWlPOGNlWG5ha2JhTTd6akMtamlwSmlPd05rUTVpdyIpOwogIGJnID0gbG9hZEltYWdlKCJodHRwczovL2Fyd2VhdmUubmV0L2xZVUZXLXZSVU5ZWkMwTWx3NDVfdXVhXzlLekNGNHZ3RTMtYVJVLTVTVkkiKTsKICBzcGVlZCA9IHJhbmRvbSgxLDUpOwogIGZsaXBzcGVlZCA9IGZsaXBzcGVlZCAqIHJhbmRvbSgyLDgpOwp9CgpmdW5jdGlvbiBzZXR1cCgpIHsKICBjcmVhdGVDYW52YXMoNjQ4LCA5NDksIFdFQkdMKTsKfQoKZnVuY3Rpb24gZHJhdygpIHsKICBiYWNrZ3JvdW5kKDIwMCk7CiAgaW1hZ2UoYmcsIC0zMjQsIC00NzQsIHdpZHRoLCBoZWlnaHQpOyAgCiAgbm9TdHJva2UoKTsKICAKICB4ID0geCArIHNzcGVlZCAqIHhkaXI7CiAgeSA9IHkgKyBzc3BlZWQgKMy iB5ZGlyOwogIAogIGlmICh4ID4gMzI0IC0gMjQwIHx8IHggPCAtMzI0LSAxMjUpIHsKICAgIHhkaXIgPSAteGRpcjsKICB9CiAgaWYgKHkgPiA0NzQgLTQwIHx8IHkgPCAtNDc0KyAxMDApIHsKICAgIHlkaXIgPSAteWRpcjsKICB9CiAgCiAgcHVzaCgpOwogIHRyYW5zbGF0ZSh4LCB5LCA2NCk7CiAgcm90YXRlWChmcmFtZUNvdW50ICogZmxpcHNwZWVkKTsKICB0ZXh0dXJlKGxvZ28pOwogIGNpcmNsZSgyMDAsIDEsIDEyMCk7CiAgcG9wKCk7Cn0"></script></head><body></body></html> TUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUa AUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU FPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU TUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUa AUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU FPUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU -=-157:::#+?D?8C49:7 %77777777777777777777777777777777777777777777777777 EDDEULDDDDEDDDDDDDEDDDDDDDU wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww,x FFnFFFFFnFFFFFFFFnFFB23r=70\Y -=-157:::#+?D?8C49:7 %77777777777777777777777777777777777777777777777777 6t[6t[6t[6t[6t[6t[6t[6t[6t[6t[6t[6t[6t[6tQ Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:sM tEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:54:06+03:00" xmp:ModifyDate="2023-02-02T23:54:06+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:9f3a26db-23bc-dd4b-a512-5cd8740720ca" xmpMM:DocumentID="adobe:docid:photoshop:2eee3dd7-b1fd-c642-bc56-456b9816b23e" xmpMMM :OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.M 0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:a4d6113d-3915-e74e-8722-c2cb14030300" stEvt:when="2023-02-02T23:54:06+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:9f3a26db-M 23bc-dd4b-a512-5cd8740720ca" stEvt:when="2023-02-02T23:54:06+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:a4d6113d-3915-e74e-8722-c2cb14030300" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocM umentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:M RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02TM 23:52:18+03:00" xmp:ModifyDate="2023-02-02T23:52:18+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:7cfe2aee-67e1-2f40-bff1-e1ea0deb3c39" xmpMM:DocumentID="adobe:docid:photoshop:5bbc60b8-85d3-9a44-85f0-23fb366c60d0" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00M " stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:8e57fc9d-b22e-574d-bc4b-b36fa9e58227" stEvt:when="2023-02-02T23:52:18+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="frM om application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:7cfe2aee-67e1-2f40-bff1-e1ea0deb3c39" stEvt:when="2023-02-02T23:52:18+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:8e57fc9d-b22e-574d-bc4b-b36fa9e58227" stRef:documentID="adobe:docid:photoshopM :6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB cM .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 FjDOUT:8C972A17ED87893EA29AF95AD948315AE182F5A21A82E4ECCEBF39EF2627FA8C CjA=:BNB.BNB:bnb1c667cxycg4cgjt7ae4c3cgj9agtmzdrnw3la90:4247275:te:0 CjA=:ETH.ETH:0xCBe879c445DDdE04A71a1b58892E26f3A27A43F7:5802230:te:0 <svg width='1024' height='1024' id='token-0' xmlns='http://www.w3.org/2000/svg'> <style>#token-0{shape-rendering: crispedges;} rect{width:32px;height:32px}.a{fill:#7739d1}.b{fill:#7ec292}.c{fill:#22133d}.d{fill:#e14d9c}.w{fill:#ffffff}</style><rect x='0' y='0' class='a'/><rect x='32' y='0' class='a'/><rect x='64' y='0' class='a'/><rect x='96' y='0' class='a'/><rect x='128' y='0' class='a'/><rect x='160' y='0' class='a'/><rect x='192' y='0' class='a'/><rect x='224' y='0' class='a'/><rect x='256' y='0' class='a'/><reM ct x='288' y='0' class='a'/><rect x='320' y='0' class='a'/><rect x='352' y='0' class='b'/><rect x='384' y='0' class='b'/><rect x='416' y='0' class='b'/><rect x='448' y='0' class='b'/><rect x='480' y='0' class='b'/><rect x='512' y='0' class='b'/><rect x='544' y='0' class='b'/><rect x='576' y='0' class='b'/><rect x='608' y='0' class='b'/><rect x='640' y='0' class='b'/><rect x='672' y='0' class='b'/><rect x='704' y='0' class='b'/><rect x='736' y='0' class='b'/><rect x='768' y='0' class='b'/><rect x='800' y='0' class='M b'/><rect x='832' y='0' class='b'/><rect x='864' y='0' class='b'/><rect x='896' y='0' class='b'/><rect x='928' y='0' class='b'/><rect x='960' y='0' class='b'/><rect x='992' y='0' class='b'/><rect x='0' y='32' class='a'/><rect x='32' y='32' class='a'/><rect x='64' y='32' class='a'/><rect x='96' y='32' class='a'/><rect x='128' y='32' class='a'/><rect x='160' y='32' class='a'/><rect x='192' y='32' class='a'/><rect x='224' y='32' class='a'/><rect x='256' y='32' class='a'/><rect x='288' y='32' class='a'/><rect x='320' yM ='32' class='a'/><rect x='352' y='32' class='a'/><rect x='384' y='32' class='a'/><rect x='416' y='32' class='a'/><rect x='448' y='32' class='a'/><rect x='480' y='32' class='a'/><rect x='512' y='32' class='a'/><rect x='544' y='32' class='a'/><rect x='576' y='32' class='a'/><rect x='608' y='32' class='a'/><rect x='640' y='32' class='a'/><rect x='672' y='32' class='w'/><rect x='704' y='32' class='a'/><rect x='736' y='32' class='b'/><rect x='768' y='32' class='b'/><rect x='800' y='32' class='b'/><rect x='832' y='32' clM ass='b'/><rect x='864' y='32' class='b'/><rect x='896' y='32' class='b'/><rect x='928' y='32' class='b'/><rect x='960' y='32' class='b'/><rect x='992' y='32' class='b'/><rect x='0' y='64' class='b'/><rect x='32' y='64' class='b'/><rect x='64' y='64' class='b'/><rect x='96' y='64' class='b'/><rect x='128' y='64' class='b'/><rect x='160' y='64' class='b'/><rect x='192' y='64' class='b'/><rect x='224' y='64' class='b'/><rect x='256' y='64' class='a'/><rect x='288' y='64' class='a'/><rect x='320' y='64' class='a'/><recM t x='352' y='64' class='a'/><rect x='384' y='64' class='a'/><rect x='416' y='64' class='c'/><rect x='448' y='64' class='c'/><rect x='480' y='64' class='c'/><rect x='512' y='64' class='c'/><rect x='544' y='64' class='c'/><rect x='576' y='64' class='c'/><rect x='608' y='64' class='c'/><rect x='640' y='64' class='c'/><rect x='672' y='64' class='a'/><rect x='704' y='64' class='a'/><rect x='736' y='64' class='a'/><rect x='768' y='64' class='a'/><rect x='800' y='64' class='a'/><rect x='832' y='64' class='a'/><rect x='864M ' y='64' class='a'/><rect x='896' y='64' class='a'/><rect x='928' y='64' class='a'/><rect x='960' y='64' class='a'/><rect x='992' y='64' class='a'/><rect x='0' y='96' class='b'/><rect x='32' y='96' class='b'/><rect x='64' y='96' class='b'/><rect x='96' y='96' class='b'/><rect x='128' y='96' class='b'/><rect x='160' y='96' class='b'/><rect x='192' y='96' class='b'/><rect x='224' y='96' class='b'/><rect x='256' y='96' class='b'/><rect x='288' y='96' class='b'/><rect x='320' y='96' class='b'/><rect x='352' y='96' clasM s='c'/><rect x='384' y='96' class='c'/><rect x='416' y='96' class='c'/><rect x='448' y='96' class='c'/><rect x='480' y='96' class='c'/><rect x='512' y='96' class='c'/><rect x='544' y='96' class='c'/><rect x='576' y='96' class='c'/><rect x='608' y='96' class='w'/><rect x='640' y='96' class='c'/><rect x='672' y='96' class='c'/><rect x='704' y='96' class='c'/><rect x='736' y='96' class='a'/><rect x='768' y='96' class='a'/><rect x='800' y='96' class='a'/><rect x='832' y='96' class='a'/><rect x='864' y='96' class='a'/><M rect x='896' y='96' class='a'/><rect x='928' y='96' class='a'/><rect x='960' y='96' class='a'/><rect x='992' y='96' class='a'/><rect x='0' y='128' class='a'/><rect x='32' y='128' class='w'/><rect x='64' y='128' class='a'/><rect x='96' y='128' class='a'/><rect x='128' y='128' class='b'/><rect x='160' y='128' class='b'/><rect x='192' y='128' class='b'/><rect x='224' y='128' class='b'/><rect x='256' y='128' class='b'/><rect x='288' y='128' class='b'/><rect x='320' y='128' class='c'/><rect x='352' y='128' class='c'/><rM ect x='384' y='128' class='c'/><rect x='416' y='128' class='c'/><rect x='448' y='128' class='c'/><rect x='480' y='128' class='c'/><rect x='512' y='128' class='c'/><rect x='544' y='128' class='c'/><rect x='576' y='128' class='c'/><rect x='608' y='128' class='c'/><rect x='640' y='128' class='c'/><rect x='672' y='128' class='c'/><rect x='704' y='128' class='c'/><rect x='736' y='128' class='b'/><rect x='768' y='128' class='b'/><rect x='800' y='128' class='b'/><rect x='832' y='128' class='b'/><rect x='864' y='128' classM ='b'/><rect x='896' y='128' class='b'/><rect x='928' y='128' class='b'/><rect x='960' y='128' class='a'/><rect x='992' y='128' class='a'/><rect x='0' y='160' class='a'/><rect x='32' y='160' class='a'/><rect x='64' y='160' class='a'/><rect x='96' y='160' class='a'/><rect x='128' y='160' class='a'/><rect x='160' y='160' class='a'/><rect x='192' y='160' class='a'/><rect x='224' y='160' class='a'/><rect x='256' y='160' class='a'/><rect x='288' y='160' class='a'/><rect x='320' y='160' class='c'/><rect x='352' y='160' clM ass='c'/><rect x='384' y='160' class='c'/><rect x='416' y='160' class='c'/><rect x='448' y='160' class='c'/><rect x='480' y='160' class='c'/><rect x='512' y='160' class='c'/><rect x='544' y='160' class='c'/><rect x='576' y='160' class='c'/><rect x='608' y='160' class='c'/><rect x='640' y='160' class='c'/><rect x='672' y='160' class='w'/><rect x='704' y='160' class='c'/><rect x='736' y='160' class='c'/><rect x='768' y='160' class='b'/><rect x='800' y='160' class='b'/><rect x='832' y='160' class='b'/><rect x='864' y=M '160' class='b'/><rect x='896' y='160' class='b'/><rect x='928' y='160' class='b'/><rect x='960' y='160' class='b'/><rect x='992' y='160' class='b'/><rect x='0' y='192' class='a'/><rect x='32' y='192' class='a'/><rect x='64' y='192' class='a'/><rect x='96' y='192' class='a'/><rect x='128' y='192' class='a'/><rect x='160' y='192' class='a'/><rect x='192' y='192' class='a'/><rect x='224' y='192' class='a'/><rect x='256' y='192' class='a'/><rect x='288' y='192' class='c'/><rect x='320' y='192' class='c'/><rect x='352'M y='192' class='c'/><rect x='384' y='192' class='c'/><rect x='416' y='192' class='c'/><rect x='448' y='192' class='c'/><rect x='480' y='192' class='c'/><rect x='512' y='192' class='c'/><rect x='544' y='192' class='c'/><rect x='576' y='192' class='c'/><rect x='608' y='192' class='c'/><rect x='640' y='192' class='c'/><rect x='672' y='192' class='c'/><rect x='704' y='192' class='c'/><rect x='736' y='192' class='c'/><rect x='768' y='192' class='a'/><rect x='800' y='192' class='a'/><rect x='832' y='192' class='w'/><rectM x='864' y='192' class='b'/><rect x='896' y='192' class='b'/><rect x='928' y='192' class='b'/><rect x='960' y='192' class='b'/><rect x='992' y='192' class='b'/><rect x='0' y='224' class='b'/><rect x='32' y='224' class='b'/><rect x='64' y='224' class='b'/><rect x='96' y='224' class='b'/><rect x='128' y='224' class='b'/><rect x='160' y='224' class='b'/><rect x='192' y='224' class='b'/><rect x='224' y='224' class='b'/><rect x='256' y='224' class='b'/><rect x='288' y='224' class='c'/><rect x='320' y='224' class='c'/><rM ect x='352' y='224' class='c'/><rect x='384' y='224' class='c'/><rect x='416' y='224' class='c'/><rect x='448' y='224' class='c'/><rect x='480' y='224' class='c'/><rect x='512' y='224' class='c'/><rect x='544' y='224' class='c'/><rect x='576' y='224' class='c'/><rect x='608' y='224' class='c'/><rect x='640' y='224' class='c'/><rect x='672' y='224' class='c'/><rect x='704' y='224' class='c'/><rect x='736' y='224' class='w'/><rect x='768' y='224' class='c'/><rect x='800' y='224' class='a'/><rect x='832' y='224' classM ='a'/><rect x='864' y='224' class='a'/><rect x='896' y='224' class='a'/><rect x='928' y='224' class='a'/><rect x='960' y='224' class='a'/><rect x='992' y='224' class='a'/><rect x='0' y='256' class='b'/><rect x='32' y='256' class='b'/><rect x='64' y='256' class='b'/><rect x='96' y='256' class='b'/><rect x='128' y='256' class='b'/><rect x='160' y='256' class='b'/><rect x='192' y='256' class='b'/><rect x='224' y='256' class='b'/><rect x='256' y='256' class='b'/><rect x='288' y='256' class='c'/><rect x='320' y='256' clM ass='c'/><rect x='352' y='256' class='c'/><rect x='384' y='256' class='a'/><rect x='416' y='256' class='a'/><rect x='448' y='256' class='a'/><rect x='480' y='256' class='a'/><rect x='512' y='256' class='c'/><rect x='544' y='256' class='c'/><rect x='576' y='256' class='c'/><rect x='608' y='256' class='c'/><rect x='640' y='256' class='c'/><rect x='672' y='256' class='c'/><rect x='704' y='256' class='w'/><rect x='736' y='256' class='c'/><rect x='768' y='256' class='c'/><rect x='800' y='256' class='a'/><rect x='832' y=M '256' class='a'/><rect x='864' y='256' class='a'/><rect x='896' y='256' class='a'/><rect x='928' y='256' class='a'/><rect x='960' y='256' class='a'/><rect x='992' y='256' class='a'/><rect x='0' y='288' class='a'/><rect x='32' y='288' class='a'/><rect x='64' y='288' class='a'/><rect x='96' y='288' class='a'/><rect x='128' y='288' class='a'/><rect x='160' y='288' class='a'/><rect x='192' y='288' class='a'/><rect x='224' y='288' class='a'/><rect x='256' y='288' class='b'/><rect x='288' y='288' class='c'/><rect x='320'M y='288' class='c'/><rect x='352' y='288' class='b'/><rect x='384' y='288' class='b'/><rect x='416' y='288' class='b'/><rect x='448' y='288' class='a'/><rect x='480' y='288' class='a'/><rect x='512' y='288' class='a'/><rect x='544' y='288' class='a'/><rect x='576' y='288' class='c'/><rect x='608' y='288' class='c'/><rect x='640' y='288' class='c'/><rect x='672' y='288' class='c'/><rect x='704' y='288' class='c'/><rect x='736' y='288' class='c'/><rect x='768' y='288' class='c'/><rect x='800' y='288' class='b'/><rectM x='832' y='288' class='b'/><rect x='864' y='288' class='b'/><rect x='896' y='288' class='b'/><rect x='928' y='288' class='b'/><rect x='960' y='288' class='b'/><rect x='992' y='288' class='b'/><rect x='0' y='320' class='a'/><rect x='32' y='320' class='a'/><rect x='64' y='320' class='a'/><rect x='96' y='320' class='w'/><rect x='128' y='320' class='a'/><rect x='160' y='320' class='a'/><rect x='192' y='320' class='a'/><rect x='224' y='320' class='a'/><rect x='256' y='320' class='a'/><rect x='288' y='320' class='c'/><rM ect x='320' y='320' class='b'/><rect x='352' y='320' class='b'/><rect x='384' y='320' class='b'/><rect x='416' y='320' class='b'/><rect x='448' y='320' class='b'/><rect x='480' y='320' class='b'/><rect x='512' y='320' class='a'/><rect x='544' y='320' class='a'/><rect x='576' y='320' class='a'/><rect x='608' y='320' class='c'/><rect x='640' y='320' class='c'/><rect x='672' y='320' class='c'/><rect x='704' y='320' class='c'/><rect x='736' y='320' class='c'/><rect x='768' y='320' class='c'/><rect x='800' y='320' classM ='w'/><rect x='832' y='320' class='b'/><rect x='864' y='320' class='b'/><rect x='896' y='320' class='b'/><rect x='928' y='320' class='b'/><rect x='960' y='320' class='b'/><rect x='992' y='320' class='b'/><rect x='0' y='352' class='b'/><rect x='32' y='352' class='b'/><rect x='64' y='352' class='b'/><rect x='96' y='352' class='b'/><rect x='128' y='352' class='a'/><rect x='160' y='352' class='a'/><rect x='192' y='352' class='a'/><rect x='224' y='352' class='a'/><rect x='256' y='352' class='a'/><rect x='288' y='352' clM ass='c'/><rect x='320' y='352' class='b'/><rect x='352' y='352' class='b'/><rect x='384' y='352' class='b'/><rect x='416' y='352' class='b'/><rect x='448' y='352' class='b'/><rect x='480' y='352' class='b'/><rect x='512' y='352' class='b'/><rect x='544' y='352' class='b'/><rect x='576' y='352' class='a'/><rect x='608' y='352' class='c'/><rect x='640' y='352' class='c'/><rect x='672' y='352' class='c'/><rect x='704' y='352' class='c'/><rect x='736' y='352' class='c'/><rect x='768' y='352' class='c'/><rect x='800' y=M '352' class='a'/><rect x='832' y='352' class='a'/><rect x='864' y='352' class='a'/><rect x='896' y='352' class='a'/><rect x='928' y='352' class='a'/><rect x='960' y='352' class='a'/><rect x='992' y='352' class='b'/><rect x='0' y='384' class='b'/><rect x='32' y='384' class='b'/><rect x='64' y='384' class='b'/><rect x='96' y='384' class='b'/><rect x='128' y='384' class='b'/><rect x='160' y='384' class='b'/><rect x='192' y='384' class='b'/><rect x='224' y='384' class='b'/><rect x='256' y='384' class='b'/><rect x='288'M y='384' class='c'/><rect x='320' y='384' class='b'/><rect x='352' y='384' class='b'/><rect x='384' y='384' class='w'/><rect x='416' y='384' class='b'/><rect x='448' y='384' class='b'/><rect x='480' y='384' class='b'/><rect x='512' y='384' class='b'/><rect x='544' y='384' class='b'/><rect x='576' y='384' class='b'/><rect x='608' y='384' class='c'/><rect x='640' y='384' class='c'/><rect x='672' y='384' class='c'/><rect x='704' y='384' class='c'/><rect x='736' y='384' class='c'/><rect x='768' y='384' class='c'/><rectM x='800' y='384' class='a'/><rect x='832' y='384' class='a'/><rect x='864' y='384' class='a'/><rect x='896' y='384' class='a'/><rect x='928' y='384' class='w'/><rect x='960' y='384' class='a'/><rect x='992' y='384' class='a'/><rect x='0' y='416' class='a'/><rect x='32' y='416' class='b'/><rect x='64' y='416' class='b'/><rect x='96' y='416' class='b'/><rect x='128' y='416' class='b'/><rect x='160' y='416' class='b'/><rect x='192' y='416' class='b'/><rect x='224' y='416' class='b'/><rect x='256' y='416' class='d'/><rM ect x='288' y='416' class='d'/><rect x='320' y='416' class='d'/><rect x='352' y='416' class='d'/><rect x='384' y='416' class='d'/><rect x='416' y='416' class='d'/><rect x='448' y='416' class='d'/><rect x='480' y='416' class='d'/><rect x='512' y='416' class='d'/><rect x='544' y='416' class='d'/><rect x='576' y='416' class='d'/><rect x='608' y='416' class='c'/><rect x='640' y='416' class='c'/><rect x='672' y='416' class='c'/><rect x='704' y='416' class='c'/><rect x='736' y='416' class='c'/><rect x='768' y='416' classM ='c'/><rect x='800' y='416' class='b'/><rect x='832' y='416' class='b'/><rect x='864' y='416' class='a'/><rect x='896' y='416' class='a'/><rect x='928' y='416' class='a'/><rect x='960' y='416' class='a'/><rect x='992' y='416' class='a'/><rect x='0' y='448' class='a'/><rect x='32' y='448' class='a'/><rect x='64' y='448' class='a'/><rect x='96' y='448' class='a'/><rect x='128' y='448' class='a'/><rect x='160' y='448' class='a'/><rect x='192' y='448' class='a'/><rect x='224' y='448' class='w'/><rect x='256' y='448' clM ass='d'/><rect x='288' y='448' class='c'/><rect x='320' y='448' class='b'/><rect x='352' y='448' class='c'/><rect x='384' y='448' class='w'/><rect x='416' y='448' class='c'/><rect x='448' y='448' class='w'/><rect x='480' y='448' class='c'/><rect x='512' y='448' class='d'/><rect x='544' y='448' class='b'/><rect x='576' y='448' class='b'/><rect x='608' y='448' class='c'/><rect x='640' y='448' class='c'/><rect x='672' y='448' class='c'/><rect x='704' y='448' class='c'/><rect x='736' y='448' class='c'/><rect x='768' y=M '448' class='c'/><rect x='800' y='448' class='w'/><rect x='832' y='448' class='b'/><rect x='864' y='448' class='b'/><rect x='896' y='448' class='b'/><rect x='928' y='448' class='b'/><rect x='960' y='448' class='b'/><rect x='992' y='448' class='b'/><rect x='0' y='480' class='a'/><rect x='32' y='480' class='w'/><rect x='64' y='480' class='a'/><rect x='96' y='480' class='a'/><rect x='128' y='480' class='a'/><rect x='160' y='480' class='a'/><rect x='192' y='480' class='a'/><rect x='224' y='480' class='a'/><rect x='256'M y='480' class='d'/><rect x='288' y='480' class='b'/><rect x='320' y='480' class='c'/><rect x='352' y='480' class='d'/><rect x='384' y='480' class='d'/><rect x='416' y='480' class='d'/><rect x='448' y='480' class='c'/><rect x='480' y='480' class='b'/><rect x='512' y='480' class='d'/><rect x='544' y='480' class='b'/><rect x='576' y='480' class='b'/><rect x='608' y='480' class='c'/><rect x='640' y='480' class='c'/><rect x='672' y='480' class='c'/><rect x='704' y='480' class='c'/><rect x='736' y='480' class='c'/><rectM x='768' y='480' class='w'/><rect x='800' y='480' class='b'/><rect x='832' y='480' class='b'/><rect x='864' y='480' class='b'/><rect x='896' y='480' class='b'/><rect x='928' y='480' class='b'/><rect x='960' y='480' class='b'/><rect x='992' y='480' class='b'/><rect x='0' y='512' class='b'/><rect x='32' y='512' class='b'/><rect x='64' y='512' class='b'/><rect x='96' y='512' class='b'/><rect x='128' y='512' class='b'/><rect x='160' y='512' class='b'/><rect x='192' y='512' class='b'/><rect x='224' y='512' class='b'/><rM ect x='256' y='512' class='d'/><rect x='288' y='512' class='d'/><rect x='320' y='512' class='d'/><rect x='352' y='512' class='d'/><rect x='384' y='512' class='b'/><rect x='416' y='512' class='d'/><rect x='448' y='512' class='d'/><rect x='480' y='512' class='d'/><rect x='512' y='512' class='d'/><rect x='544' y='512' class='b'/><rect x='576' y='512' class='b'/><rect x='608' y='512' class='c'/><rect x='640' y='512' class='c'/><rect x='672' y='512' class='c'/><rect x='704' y='512' class='c'/><rect x='736' y='512' classM ='c'/><rect x='768' y='512' class='c'/><rect x='800' y='512' class='a'/><rect x='832' y='512' class='a'/><rect x='864' y='512' class='a'/><rect x='896' y='512' class='a'/><rect x='928' y='512' class='a'/><rect x='960' y='512' class='a'/><rect x='992' y='512' class='a'/><rect x='0' y='544' class='b'/><rect x='32' y='544' class='b'/><rect x='64' y='544' class='b'/><rect x='96' y='544' class='b'/><rect x='128' y='544' class='b'/><rect x='160' y='544' class='b'/><rect x='192' y='544' class='b'/><rect x='224' y='544' clM ass='b'/><rect x='256' y='544' class='b'/><rect x='288' y='544' class='c'/><rect x='320' y='544' class='b'/><rect x='352' y='544' class='b'/><rect x='384' y='544' class='b'/><rect x='416' y='544' class='b'/><rect x='448' y='544' class='b'/><rect x='480' y='544' class='b'/><rect x='512' y='544' class='b'/><rect x='544' y='544' class='b'/><rect x='576' y='544' class='b'/><rect x='608' y='544' class='c'/><rect x='640' y='544' class='c'/><rect x='672' y='544' class='c'/><rect x='704' y='544' class='c'/><rect x='736' y=M '544' class='c'/><rect x='768' y='544' class='c'/><rect x='800' y='544' class='a'/><rect x='832' y='544' class='a'/><rect x='864' y='544' class='a'/><rect x='896' y='544' class='a'/><rect x='928' y='544' class='a'/><rect x='960' y='544' class='a'/><rect x='992' y='544' class='a'/><rect x='0' y='576' class='a'/><rect x='32' y='576' class='a'/><rect x='64' y='576' class='a'/><rect x='96' y='576' class='a'/><rect x='128' y='576' class='b'/><rect x='160' y='576' class='b'/><rect x='192' y='576' class='b'/><rect x='224'M y='576' class='b'/><rect x='256' y='576' class='b'/><rect x='288' y='576' class='c'/><rect x='320' y='576' class='b'/><rect x='352' y='576' class='a'/><rect x='384' y='576' class='a'/><rect x='416' y='576' class='a'/><rect x='448' y='576' class='b'/><rect x='480' y='576' class='b'/><rect x='512' y='576' class='b'/><rect x='544' y='576' class='b'/><rect x='576' y='576' class='c'/><rect x='608' y='576' class='c'/><rect x='640' y='576' class='c'/><rect x='672' y='576' class='c'/><rect x='704' y='576' class='c'/><rectM x='736' y='576' class='c'/><rect x='768' y='576' class='c'/><rect x='800' y='576' class='b'/><rect x='832' y='576' class='b'/><rect x='864' y='576' class='b'/><rect x='896' y='576' class='b'/><rect x='928' y='576' class='b'/><rect x='960' y='576' class='w'/><rect x='992' y='576' class='a'/><rect x='0' y='608' class='a'/><rect x='32' y='608' class='a'/><rect x='64' y='608' class='a'/><rect x='96' y='608' class='a'/><rect x='128' y='608' class='a'/><rect x='160' y='608' class='a'/><rect x='192' y='608' class='w'/><rM ect x='224' y='608' class='a'/><rect x='256' y='608' class='a'/><rect x='288' y='608' class='c'/><rect x='320' y='608' class='b'/><rect x='352' y='608' class='b'/><rect x='384' y='608' class='b'/><rect x='416' y='608' class='b'/><rect x='448' y='608' class='b'/><rect x='480' y='608' class='b'/><rect x='512' y='608' class='b'/><rect x='544' y='608' class='c'/><rect x='576' y='608' class='c'/><rect x='608' y='608' class='c'/><rect x='640' y='608' class='c'/><rect x='672' y='608' class='c'/><rect x='704' y='608' classM ='c'/><rect x='736' y='608' class='w'/><rect x='768' y='608' class='w'/><rect x='800' y='608' class='b'/><rect x='832' y='608' class='b'/><rect x='864' y='608' class='b'/><rect x='896' y='608' class='b'/><rect x='928' y='608' class='b'/><rect x='960' y='608' class='b'/><rect x='992' y='608' class='b'/><rect x='0' y='640' class='b'/><rect x='32' y='640' class='a'/><rect x='64' y='640' class='a'/><rect x='96' y='640' class='a'/><rect x='128' y='640' class='a'/><rect x='160' y='640' class='a'/><rect x='192' y='640' clM ass='a'/><rect x='224' y='640' class='c'/><rect x='256' y='640' class='c'/><rect x='288' y='640' class='c'/><rect x='320' y='640' class='c'/><rect x='352' y='640' class='b'/><rect x='384' y='640' class='b'/><rect x='416' y='640' class='b'/><rect x='448' y='640' class='b'/><rect x='480' y='640' class='c'/><rect x='512' y='640' class='w'/><rect x='544' y='640' class='c'/><rect x='576' y='640' class='w'/><rect x='608' y='640' class='c'/><rect x='640' y='640' class='c'/><rect x='672' y='640' class='c'/><rect x='704' y=M '640' class='c'/><rect x='736' y='640' class='c'/><rect x='768' y='640' class='c'/><rect x='800' y='640' class='c'/><rect x='832' y='640' class='a'/><rect x='864' y='640' class='b'/><rect x='896' y='640' class='b'/><rect x='928' y='640' class='b'/><rect x='960' y='640' class='b'/><rect x='992' y='640' class='b'/><rect x='0' y='672' class='b'/><rect x='32' y='672' class='b'/><rect x='64' y='672' class='b'/><rect x='96' y='672' class='w'/><rect x='128' y='672' class='b'/><rect x='160' y='672' class='b'/><rect x='192'M y='672' class='c'/><rect x='224' y='672' class='w'/><rect x='256' y='672' class='c'/><rect x='288' y='672' class='c'/><rect x='320' y='672' class='c'/><rect x='352' y='672' class='c'/><rect x='384' y='672' class='c'/><rect x='416' y='672' class='c'/><rect x='448' y='672' class='c'/><rect x='480' y='672' class='c'/><rect x='512' y='672' class='c'/><rect x='544' y='672' class='c'/><rect x='576' y='672' class='c'/><rect x='608' y='672' class='c'/><rect x='640' y='672' class='c'/><rect x='672' y='672' class='c'/><rectM x='704' y='672' class='c'/><rect x='736' y='672' class='c'/><rect x='768' y='672' class='c'/><rect x='800' y='672' class='c'/><rect x='832' y='672' class='c'/><rect x='864' y='672' class='a'/><rect x='896' y='672' class='a'/><rect x='928' y='672' class='a'/><rect x='960' y='672' class='a'/><rect x='992' y='672' class='a'/><rect x='0' y='704' class='b'/><rect x='32' y='704' class='b'/><rect x='64' y='704' class='b'/><rect x='96' y='704' class='b'/><rect x='128' y='704' class='b'/><rect x='160' y='704' class='c'/><rM ect x='192' y='704' class='c'/><rect x='224' y='704' class='c'/><rect x='256' y='704' class='c'/><rect x='288' y='704' class='c'/><rect x='320' y='704' class='c'/><rect x='352' y='704' class='w'/><rect x='384' y='704' class='c'/><rect x='416' y='704' class='c'/><rect x='448' y='704' class='c'/><rect x='480' y='704' class='c'/><rect x='512' y='704' class='c'/><rect x='544' y='704' class='c'/><rect x='576' y='704' class='c'/><rect x='608' y='704' class='c'/><rect x='640' y='704' class='c'/><rect x='672' y='704' classM ='c'/><rect x='704' y='704' class='c'/><rect x='736' y='704' class='c'/><rect x='768' y='704' class='c'/><rect x='800' y='704' class='c'/><rect x='832' y='704' class='c'/><rect x='864' y='704' class='c'/><rect x='896' y='704' class='a'/><rect x='928' y='704' class='a'/><rect x='960' y='704' class='a'/><rect x='992' y='704' class='a'/><rect x='0' y='736' class='a'/><rect x='32' y='736' class='a'/><rect x='64' y='736' class='a'/><rect x='96' y='736' class='a'/><rect x='128' y='736' class='c'/><rect x='160' y='736' clM ass='c'/><rect x='192' y='736' class='c'/><rect x='224' y='736' class='c'/><rect x='256' y='736' class='c'/><rect x='288' y='736' class='c'/><rect x='320' y='736' class='c'/><rect x='352' y='736' class='w'/><rect x='384' y='736' class='c'/><rect x='416' y='736' class='c'/><rect x='448' y='736' class='c'/><rect x='480' y='736' class='c'/><rect x='512' y='736' class='c'/><rect x='544' y='736' class='c'/><rect x='576' y='736' class='c'/><rect x='608' y='736' class='c'/><rect x='640' y='736' class='c'/><rect x='672' y=M '736' class='c'/><rect x='704' y='736' class='c'/><rect x='736' y='736' class='c'/><rect x='768' y='736' class='c'/><rect x='800' y='736' class='c'/><rect x='832' y='736' class='c'/><rect x='864' y='736' class='c'/><rect x='896' y='736' class='w'/><rect x='928' y='736' class='b'/><rect x='960' y='736' class='b'/><rect x='992' y='736' class='b'/><rect x='0' y='768' class='a'/><rect x='32' y='768' class='a'/><rect x='64' y='768' class='a'/><rect x='96' y='768' class='c'/><rect x='128' y='768' class='w'/><rect x='160'M y='768' class='c'/><rect x='192' y='768' class='c'/><rect x='224' y='768' class='c'/><rect x='256' y='768' class='c'/><rect x='288' y='768' class='c'/><rect x='320' y='768' class='c'/><rect x='352' y='768' class='c'/><rect x='384' y='768' class='c'/><rect x='416' y='768' class='c'/><rect x='448' y='768' class='c'/><rect x='480' y='768' class='c'/><rect x='512' y='768' class='c'/><rect x='544' y='768' class='c'/><rect x='576' y='768' class='c'/><rect x='608' y='768' class='c'/><rect x='640' y='768' class='c'/><rectM x='672' y='768' class='c'/><rect x='704' y='768' class='c'/><rect x='736' y='768' class='c'/><rect x='768' y='768' class='c'/><rect x='800' y='768' class='c'/><rect x='832' y='768' class='c'/><rect x='864' y='768' class='c'/><rect x='896' y='768' class='c'/><rect x='928' y='768' class='b'/><rect x='960' y='768' class='b'/><rect x='992' y='768' class='b'/><rect x='0' y='800' class='b'/><rect x='32' y='800' class='b'/><rect x='64' y='800' class='b'/><rect x='96' y='800' class='c'/><rect x='128' y='800' class='c'/><rM ect x='160' y='800' class='c'/><rect x='192' y='800' class='c'/><rect x='224' y='800' class='c'/><rect x='256' y='800' class='c'/><rect x='288' y='800' class='w'/><rect x='320' y='800' class='a'/><rect x='352' y='800' class='c'/><rect x='384' y='800' class='c'/><rect x='416' y='800' class='c'/><rect x='448' y='800' class='c'/><rect x='480' y='800' class='w'/><rect x='512' y='800' class='c'/><rect x='544' y='800' class='c'/><rect x='576' y='800' class='c'/><rect x='608' y='800' class='c'/><rect x='640' y='800' classM ='c'/><rect x='672' y='800' class='w'/><rect x='704' y='800' class='c'/><rect x='736' y='800' class='c'/><rect x='768' y='800' class='c'/><rect x='800' y='800' class='c'/><rect x='832' y='800' class='c'/><rect x='864' y='800' class='c'/><rect x='896' y='800' class='c'/><rect x='928' y='800' class='a'/><rect x='960' y='800' class='a'/><rect x='992' y='800' class='b'/><rect x='0' y='832' class='b'/><rect x='32' y='832' class='b'/><rect x='64' y='832' class='c'/><rect x='96' y='832' class='c'/><rect x='128' y='832' clM ass='c'/><rect x='160' y='832' class='c'/><rect x='192' y='832' class='c'/><rect x='224' y='832' class='c'/><rect x='256' y='832' class='w'/><rect x='288' y='832' class='a'/><rect x='320' y='832' class='c'/><rect x='352' y='832' class='c'/><rect x='384' y='832' class='c'/><rect x='416' y='832' class='c'/><rect x='448' y='832' class='c'/><rect x='480' y='832' class='c'/><rect x='512' y='832' class='c'/><rect x='544' y='832' class='c'/><rect x='576' y='832' class='c'/><rect x='608' y='832' class='c'/><rect x='640' y=M '832' class='c'/><rect x='672' y='832' class='w'/><rect x='704' y='832' class='c'/><rect x='736' y='832' class='c'/><rect x='768' y='832' class='c'/><rect x='800' y='832' class='c'/><rect x='832' y='832' class='c'/><rect x='864' y='832' class='c'/><rect x='896' y='832' class='c'/><rect x='928' y='832' class='c'/><rect x='960' y='832' class='a'/><rect x='992' y='832' class='a'/><rect x='0' y='864' class='a'/><rect x='32' y='864' class='b'/><rect x='64' y='864' class='c'/><rect x='96' y='864' class='c'/><rect x='128'M y='864' class='c'/><rect x='160' y='864' class='c'/><rect x='192' y='864' class='c'/><rect x='224' y='864' class='c'/><rect x='256' y='864' class='c'/><rect x='288' y='864' class='a'/><rect x='320' y='864' class='w'/><rect x='352' y='864' class='c'/><rect x='384' y='864' class='c'/><rect x='416' y='864' class='c'/><rect x='448' y='864' class='c'/><rect x='480' y='864' class='c'/><rect x='512' y='864' class='c'/><rect x='544' y='864' class='c'/><rect x='576' y='864' class='c'/><rect x='608' y='864' class='c'/><rectM x='640' y='864' class='c'/><rect x='672' y='864' class='c'/><rect x='704' y='864' class='c'/><rect x='736' y='864' class='c'/><rect x='768' y='864' class='c'/><rect x='800' y='864' class='c'/><rect x='832' y='864' class='c'/><rect x='864' y='864' class='c'/><rect x='896' y='864' class='c'/><rect x='928' y='864' class='w'/><rect x='960' y='864' class='a'/><rect x='992' y='864' class='a'/><rect x='0' y='896' class='a'/><rect x='32' y='896' class='a'/><rect x='64' y='896' class='w'/><rect x='96' y='896' class='c'/><rM ect x='128' y='896' class='c'/><rect x='160' y='896' class='c'/><rect x='192' y='896' class='c'/><rect x='224' y='896' class='c'/><rect x='256' y='896' class='a'/><rect x='288' y='896' class='c'/><rect x='320' y='896' class='c'/><rect x='352' y='896' class='c'/><rect x='384' y='896' class='c'/><rect x='416' y='896' class='c'/><rect x='448' y='896' class='c'/><rect x='480' y='896' class='c'/><rect x='512' y='896' class='c'/><rect x='544' y='896' class='c'/><rect x='576' y='896' class='c'/><rect x='608' y='896' classM ='a'/><rect x='640' y='896' class='w'/><rect x='672' y='896' class='c'/><rect x='704' y='896' class='c'/><rect x='736' y='896' class='c'/><rect x='768' y='896' class='c'/><rect x='800' y='896' class='w'/><rect x='832' y='896' class='c'/><rect x='864' y='896' class='c'/><rect x='896' y='896' class='c'/><rect x='928' y='896' class='c'/><rect x='960' y='896' class='b'/><rect x='992' y='896' class='w'/><rect x='0' y='928' class='a'/><rect x='32' y='928' class='c'/><rect x='64' y='928' class='c'/><rect x='96' y='928' clM ass='c'/><rect x='128' y='928' class='c'/><rect x='160' y='928' class='c'/><rect x='192' y='928' class='c'/><rect x='224' y='928' class='c'/><rect x='256' y='928' class='c'/><rect x='288' y='928' class='c'/><rect x='320' y='928' class='c'/><rect x='352' y='928' class='c'/><rect x='384' y='928' class='c'/><rect x='416' y='928' class='c'/><rect x='448' y='928' class='c'/><rect x='480' y='928' class='c'/><rect x='512' y='928' class='c'/><rect x='544' y='928' class='c'/><rect x='576' y='928' class='c'/><rect x='608' y=M '928' class='a'/><rect x='640' y='928' class='c'/><rect x='672' y='928' class='c'/><rect x='704' y='928' class='c'/><rect x='736' y='928' class='c'/><rect x='768' y='928' class='c'/><rect x='800' y='928' class='c'/><rect x='832' y='928' class='c'/><rect x='864' y='928' class='c'/><rect x='896' y='928' class='c'/><rect x='928' y='928' class='c'/><rect x='960' y='928' class='c'/><rect x='992' y='928' class='b'/><rect x='0' y='960' class='b'/><rect x='32' y='960' class='c'/><rect x='64' y='960' class='c'/><rect x='96'M y='960' class='c'/><rect x='128' y='960' class='c'/><rect x='160' y='960' class='c'/><rect x='192' y='960' class='c'/><rect x='224' y='960' class='c'/><rect x='256' y='960' class='c'/><rect x='288' y='960' class='c'/><rect x='320' y='960' class='c'/><rect x='352' y='960' class='c'/><rect x='384' y='960' class='c'/><rect x='416' y='960' class='c'/><rect x='448' y='960' class='c'/><rect x='480' y='960' class='c'/><rect x='512' y='960' class='c'/><rect x='544' y='960' class='c'/><rect x='576' y='960' class='c'/><rectM x='608' y='960' class='a'/><rect x='640' y='960' class='c'/><rect x='672' y='960' class='w'/><rect x='704' y='960' class='c'/><rect x='736' y='960' class='c'/><rect x='768' y='960' class='c'/><rect x='800' y='960' class='c'/><rect x='832' y='960' class='c'/><rect x='864' y='960' class='c'/><rect x='896' y='960' class='c'/><rect x='928' y='960' class='c'/><rect x='960' y='960' class='c'/><rect x='992' y='960' class='a'/><rect x='0' y='992' class='b'/><rect x='32' y='992' class='c'/><rect x='64' y='992' class='c'/><M rect x='96' y='992' class='c'/><rect x='128' y='992' class='c'/><rect x='160' y='992' class='c'/><rect x='192' y='992' class='c'/><rect x='224' y='992' class='c'/><rect x='256' y='992' class='c'/><rect x='288' y='992' class='c'/><rect x='320' y='992' class='w'/><rect x='352' y='992' class='c'/><rect x='384' y='992' class='c'/><rect x='416' y='992' class='c'/><rect x='448' y='992' class='c'/><rect x='480' y='992' class='c'/><rect x='512' y='992' class='c'/><rect x='544' y='992' class='c'/><rect x='576' y='992' classM ='c'/><rect x='608' y='992' class='a'/><rect x='640' y='992' class='c'/><rect x='672' y='992' class='c'/><rect x='704' y='992' class='c'/><rect x='736' y='992' class='c'/><rect x='768' y='992' class='c'/><rect x='800' y='992' class='c'/><rect x='832' y='992' class='c'/><rect x='864' y='992' class='c'/><rect x='896' y='992' class='c'/><rect x='928' y='992' class='c'/><rect x='960' y='992' class='c'/><rect x='992' y='992' class='a'/></svg>h! FjDOUT:316745776DAC98DBDDBA55452770A9873F50CB616BBF46035947A7C9047DDC78 FjDOUT:CF0AEEBF11ED85F053B30A00A9387C525F6AC1B04E7F9F5B414E1EE2B7AD731B FjDOUT:793615EA4A28FEEDE4434958328BC339F0B118336CE9B5EADC154BD82788D066 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobM e.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:02:05+03:00" xmp:ModifyDate="2023-02-03T00:02:05+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:45cabdffM -4207-df4b-b6b3-6c233d7e2621" xmpMM:DocumentID="adobe:docid:photoshop:005f273f-e699-e54f-b5a4-7ba86f0904f4" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4M a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:0a4573c8-a0cf-9343-80b3-cb89ab2d704c" stEvt:when="2023-02-03T00:02:05+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from M application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:45cabdff-4207-df4b-b6b3-6c233d7e2621" stEvt:when="2023-02-03T00:02:05+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:0a4573c8-a0cf-9343-80b3-cb89ab2d704c" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <phM otoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ce Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/M 1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:52:03+03:00" xmp:ModifyDate="2023-02-02T23:52:03+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:e89826ca-a5b9-ab4a-b718-a76e97afe4c5" xmpMM:DocumentID="adobe:docid:photoshop:f7806049-f914-bc4f-95b3-738M bf5680d34" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="AdoM be Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:32e4ff7e-b6bc-384e-9b53-e727cb28e034" stEvt:when="2023-02-02T23:52:03+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xM mp.iid:e89826ca-a5b9-ab4a-b718-a76e97afe4c5" stEvt:when="2023-02-02T23:52:03+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:32e4ff7e-b6bc-384e-9b53-e727cb28e034" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag>M </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xapM " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#"M xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:02:19+03:00" xmp:ModifyDate="2023-02-03T00:02:19+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:8bcb3def-477b-3e4b-bbea-62463269e8b2" xmpMM:DocumentID="adobe:docid:photoshop:206f96d2-41f1-7843-9267-7dc2fa4f60d1" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGM B IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:20cc94cd-c79c-3a4e-be73-c2c3M 7b13ed63" stEvt:when="2023-02-03T00:02:19+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:8bcb3def-477b-3e4b-bbea-62463269e8b2" stEvt:when="2023-02-03T00:02:19+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (WindoM ws)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:20cc94cd-c79c-3a4e-be73-c2c37b13ed63" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool /ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sTM ype/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:06:54+03:00" xmp:ModifyDate="2023-02-03T00:06:54+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:e3727b60-c3b9-1f47-90c6-ae59e006a04d" xmpMM:DocumentID="adobe:docid:photoshop:9582e418-4564-804b-8292-1a3598f268df" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshoM p:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:45502dde-f7M da-614c-97fc-866ba557b96d" stEvt:when="2023-02-03T00:06:54+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:e3727b60-c3b9-1f47-90c6-ae59e006a04d" stEvt:when="2023-02-03T00:06:54+03:00" stEvt:softwareAgent="Adobe PhotM oshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:45502dde-f7da-614c-97fc-866ba557b96d" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 c/Foundry USA Pool #dropgold/ FjDOUT:2F70FAA86C1797B51BFD88047E55253CBA975691F53158942F12A28131ADD7CA Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.M org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:54:23+03:00" xmp:ModifyDatM e="2023-02-02T23:54:23+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:72b5c9e2-410c-6844-a7aa-c82e789f9d04" xmpMM:DocumentID="adobe:docid:photoshop:ccab23d6-067b-ba40-86b3-a87678e6db8f" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe M Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:6c7aa775-90d6-f84c-b8b9-c5851e284c44" stEvt:when="2023-02-02T23:54:23+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photM oshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:72b5c9e2-410c-6844-a7aa-c82e789f9d04" stEvt:when="2023-02-02T23:54:23+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:6c7aa775-90d6-f84c-b8b9-c5851e284c44" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecM b72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:metM a/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 M (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:57:17+03:00" xmp:ModifyDate="2023-02-02T23:57:17+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:300f3f52-f940-3942-b7ec-0181f605c5b7" xmpMM:DocumentID="adobe:docid:photoshop:39b9dc53-c08d-2d41-9625-029be6fe7725" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="M xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:05e029ec-82d4-334b-853d-585c8f79c8d9" stEvt:when="2023-02-02T23:57:17+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (M Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:300f3f52-f940-3942-b7ec-0181f605c5b7" stEvt:when="2023-02-02T23:57:17+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmM p.iid:05e029ec-82d4-334b-853d-585c8f79c8d9" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlnsM :rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:53:50+M 03:00" xmp:ModifyDate="2023-02-02T23:53:50+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:345891fc-b1fd-ea4f-b4b3-9e11605e4e06" xmpMM:DocumentID="adobe:docid:photoshop:7a36ea52-4076-fa46-93ab-bd36f4c3bef2" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:sM oftwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:8f9614ab-aa73-3b49-8a09-1b51fe5ea7bc" stEvt:when="2023-02-02T23:53:50+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from applicM ation/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:345891fc-b1fd-ea4f-b4b3-9e11605e4e06" stEvt:when="2023-02-02T23:53:50+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:8f9614ab-aa73-3b49-8a09-1b51fe5ea7bc" stRef:documentID="adobe:docid:photoshop:6564fe94M -253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour spaM .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-0M 1:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MeM tadataDate="2023-02-02T23:55:24+03:00" xmp:ModifyDate="2023-02-02T23:55:24+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:3f013ffb-0418-3247-9f7a-8182fa170d54" xmpMM:DocumentID="adobe:docid:photoshop:c62cea25-cacd-a549-8c2d-bb049acf81f6" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="20M 23-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:5a664466-4488-4b43-b232-426af7b439bd" stEvt:when="2023-02-02T23:55:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converteM d" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3f013ffb-0418-3247-9f7a-8182fa170d54" stEvt:when="2023-02-02T23:55:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:5a664466-4488-4b43-b232-426af7b439bd" stRef:documentIDM ="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:nM s:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop M 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:58:56+03:00" xmp:ModifyDate="2023-02-02T23:58:56+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:c750450a-f0dc-8845-9293-0018795bb15b" xmpMM:DocumentID="adobe:docid:photoshop:56d76642-317f-7944-a707-d256cf7051a6" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instancM eID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:66937cef-872c-1a47-8bd7-bcfd12b7e710" stEvt:when="2023-02-02T23:58:56+03:00" stEvt:softwareAgent="Adobe Photoshop 2M 1.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:c750450a-f0dc-8845-9293-0018795bb15b" stEvt:when="2023-02-02T23:58:56+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceIM D="xmp.iid:66937cef-872c-1a47-8bd7-bcfd12b7e710" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.comM /xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:00:59+03:00" xmp:ModifyDate="2023-02-03T00:00:59+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:37756a80-1bffM -7e4c-ab67-2067a635bcfa" xmpMM:DocumentID="adobe:docid:photoshop:991ffd88-3c75-264c-88e3-a12ae24e6c22" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-M 9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:280d0e31-6393-4749-91f1-afd8f349d987" stEvt:when="2023-02-03T00:00:59+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from appliM cation/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:37756a80-1bff-7e4c-ab67-2067a635bcfa" stEvt:when="2023-02-03T00:00:59+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:280d0e31-6393-4749-91f1-afd8f349d987" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshM op:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> right (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rM df:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-M 03T00:06:33+03:00" xmp:ModifyDate="2023-02-03T00:06:33+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:f2af3ff0-524e-4e45-b3e6-1dc9143a98bf" xmpMM:DocumentID="adobe:docid:photoshop:841f19f2-7238-2c49-801f-d15b0b3040ae" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03M :00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:599ad2d2-b7c4-6947-b2cd-bfc6be93bb71" stEvt:when="2023-02-03T00:06:33+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters=M "from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:f2af3ff0-524e-4e45-b3e6-1dc9143a98bf" stEvt:when="2023-02-03T00:06:33+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:599ad2d2-b7c4-6947-b2cd-bfc6be93bb71" stRef:documentID="adobe:docid:photosM hop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGM B colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvtM ="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:52:51+03:00" xmp:ModifyDate="2023-02-02T23:52:51+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:dfcd1683-a3cf-6640-af66-1390d523d847" xmpMM:DocumentID="adobe:docid:photoshop:eb1a31ba-f8a7-dd4d-8957-40f9ecf59e58" xmpMM:OriM ginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (WM indows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:f2e5ebba-5c49-ed4d-add8-67d5bdc9c690" stEvt:when="2023-02-02T23:52:51+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:dfcd1683-a3cfM -6640-af66-1390d523d847" stEvt:when="2023-02-02T23:52:51+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:f2e5ebba-5c49-ed4d-add8-67d5bdc9c690" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumenM tAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceM Event#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:43:29+03:00" xmp:ModifyDate="2023-02-02T23:43:29+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:12f65ef3-690b-9b43-9519-708015e4abf3" xmpMM:DocumentID="adobe:docid:photoshop:8b809d31-273a-464d-b84c-2ac820d51294" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-M ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:aM ction="saved" stEvt:instanceID="xmp.iid:1f6ebb91-4de9-b047-b6f6-1e619c243cc4" stEvt:when="2023-02-02T23:43:29+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:12f65ef3-690b-9b43-9519-708015e4abf3" stEvt:when="2023-02M -02T23:43:29+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:1f6ebb91-4de9-b047-b6f6-1e619c243cc4" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </M x:xmpmeta> M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-sM yntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:59:12+03:00" xmp:ModifyDate="2023-02-02T23:59:M 12+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:98d90746-80e9-5f40-9618-ce0fdd1075e2" xmpMM:DocumentID="adobe:docid:photoshop:d8b9a132-d22f-0e4c-a351-96f97d08cec6" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (WindM ows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:158f808b-4dd7-2e4c-a3bc-3aef1a659719" stEvt:when="2023-02-02T23:59:12+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"M /> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:98d90746-80e9-5f40-9618-ce0fdd1075e2" stEvt:when="2023-02-02T23:59:12+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:158f808b-4dd7-2e4c-a3bc-3aef1a659719" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:origM inalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 DefauM lt RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1M /" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:59:29+03:00" xmp:ModifyDate="2023-02-02T23:59:29+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:bf96df4e-d1a0-7c48-b182-43e212eab950" xmpMM:DocumentID="adobe:docM id:photoshop:f36cb407-b10e-e448-803c-d28ba26208c6" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31TM 21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:988a3851-5c73-7e43-a5c6-d8ffcad57c98" stEvt:when="2023-02-02T23:59:29+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li M stEvt:action="saved" stEvt:instanceID="xmp.iid:bf96df4e-d1a0-7c48-b182-43e212eab950" stEvt:when="2023-02-02T23:59:29+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:988a3851-5c73-7e43-a5c6-d8ffcad57c98" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DAM 26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.M com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:58:34+03:00" xmp:ModifyDate="2023-02-02T23:58:34+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:3e358e3d-8628-784f-ae01-e685a6828b49" xmpMM:DocumentID="adobe:docid:photoshop:61142c2f-d925-0b49-M acaf-9508d5f41426" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgM ent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:c8131d5c-00c5-b54c-b7b8-bd2deac80eb5" stEvt:when="2023-02-02T23:58:34+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instaM nceID="xmp.iid:3e358e3d-8628-784f-ae01-e685a6828b49" stEvt:when="2023-02-02T23:58:34+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:c8131d5c-00c5-b54c-b7b8-bd2deac80eb5" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </M rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2M Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#">M <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:54:42+03:00" xmp:ModifyDate="2023-02-02T23:54:42+03:00" dM c:format="image/jpeg" xmpMM:InstanceID="xmp.iid:711a4496-d8c1-6f42-bafb-75311d59e100" xmpMM:DocumentID="adobe:docid:photoshop:87f964d3-5dbf-d349-8708-65028cfbd658" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdM f:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:4717a7cd-7d84-7d47-933d-09cf2f28ba0b" stEvt:when="2023-02-02T23:54:42+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li M stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:711a4496-d8c1-6f42-bafb-75311d59e100" stEvt:when="2023-02-02T23:54:42+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:4717a7cd-7d84-7d47-933d-09cf2f28ba0b" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumenM tID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB coloM ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="httM p://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:01:39+03:00" xmp:ModifyDate="2023-02-03T00:01:39+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iM id:758bcf87-913c-9f48-9c0a-610b755de106" xmpMM:DocumentID="adobe:docid:photoshop:2ae77fd8-d2eb-fd41-81ed-17bda9ddb505" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xM mp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:14cc0a9e-c7e9-a448-b8f2-fbca8cc39566" stEvt:when="2023-02-03T00:01:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="convM erted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:758bcf87-913c-9f48-9c0a-610b755de106" stEvt:when="2023-02-03T00:01:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:14cc0a9e-c7e9-a448-b8f2-fbca8cc39566" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf511M 0288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/RM esourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:58:09+03:00" xmp:ModifyDate="2023-02-02T23:58:09+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:ba43e5dc-cb0d-5945-99a2-017a15cf54d5" xmpMM:DocumentID="adobe:docid:photoshop:93018a75-a72e-8042-8b40-7bb27c47aac2" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCM Profile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:0d05fed8-3b7e-93M 4c-b022-b0991e20cb52" stEvt:when="2023-02-02T23:58:09+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:ba43e5dc-cb0d-5945-99a2-017a15cf54d5" stEvt:when="2023-02-02T23:58:09+03:00" stEvt:softwareAgent="Adobe PhotoshopM 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:0d05fed8-3b7e-934c-b022-b0991e20cb52" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://M ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:56:02+03:00" xmp:ModifyDate="2023-02-02T23:56:02+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:259811d4-aae9-2f43-a591-5ba2b1c96f83" xmpMM:DocumentID="adobe:docid:photoshop:e98e238f-b276-0e42-bbe2-effa610a90e5" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshopM :ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceM ID="xmp.iid:57273942-ce1c-e043-aa1b-363ddc6693da" stEvt:when="2023-02-02T23:56:02+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:259811d4-aae9-2f43-a591-5ba2b1c96f83" stEvt:when="2023-02-02T23:56:02+03:00" stEvt:soM ftwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:57273942-ce1c-e043-aa1b-363ddc6693da" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-nsM #"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:55:40+03:00" xmp:ModifyDate="2023-02-02T23:55:40+03:00M " dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:b23a486a-67c6-8447-85ec-c0bcd8ab0c09" xmpMM:DocumentID="adobe:docid:photoshop:ffb50835-367b-c04a-8f7f-3346e5badcc5" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> M <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:28cc80d7-ea53-564d-947d-292151663727" stEvt:when="2023-02-02T23:55:40+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:M li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:b23a486a-67c6-8447-85ec-c0bcd8ab0c09" stEvt:when="2023-02-02T23:55:40+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:28cc80d7-ea53-564d-947d-292151663727" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocuM mentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB cM ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0M " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlM ns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:44:04+03:00" xmp:ModifyDate="2023-02-02T23:44:04+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:fc9c8046-f0e2-5749-b6da-0ba6886eb704" xmpMM:DocumentID="adobe:docid:photoshop:45ba5af2-272a-2d47-a98a-29e815a19bc0" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEM C61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:f41ff474-efb9-764c-884b-10bd7253M a03c" stEvt:when="2023-02-02T23:44:04+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:fc9c8046-f0e2-5749-b6da-0ba6886eb704" stEvt:when="2023-02-02T23:44:04+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"M stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:f41ff474-efb9-764c-884b-10bd7253a03c" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/M ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:50:53+03:00" xmp:ModifyDate="2023-02-02T23:50:53+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:f769e145-7cb5-cd42-af6f-0cd3d8a15aa6" xmpMM:DocumentID="adobe:docid:photoshop:7a1d50ce-8133-d746-a215-77cbdf590b51" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICM CProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:662bc714-d276-7M 343-a114-c99b13d7850f" stEvt:when="2023-02-02T23:50:53+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:f769e145-7cb5-cd42-af6f-0cd3d8a15aa6" stEvt:when="2023-02-02T23:50:53+03:00" stEvt:softwareAgent="Adobe PhotoshoM p 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:662bc714-d276-7343-a114-c99b13d7850f" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourcM eEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:59:47+03:00" xmp:ModifyDate="2023-02-02T23:59:47+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:26f7f70c-15d3-6d49-9f60-7a50058abcac" xmpMM:DocumentID="adobe:docid:photoshop:dfe68c1f-fb67-2845-b689-273581857813" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643M -ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:M action="saved" stEvt:instanceID="xmp.iid:00c6253b-d8f7-7242-9c76-e326f7a0c831" stEvt:when="2023-02-02T23:59:47+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:26f7f70c-15d3-6d49-9f60-7a50058abcac" stEvt:when="2023-0M 2-02T23:59:47+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:00c6253b-d8f7-7242-9c76-e326f7a0c831" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> <M /x:xmpmeta> M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResoM urceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:02:38+03:00" xmp:ModifyDate="2023-02-03T00:02:38+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:0c2b5b65-3469-ad4b-8018-01322b81e22d" xmpMM:DocumentID="adobe:docid:photoshop:5385fd80-af35-7049-975f-6cd32d406d90" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProM file="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:13281450-52c7-4240-M b6a3-62ce81f0d62a" stEvt:when="2023-02-03T00:02:38+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:0c2b5b65-3469-ad4b-8018-01322b81e22d" stEvt:when="2023-02-03T00:02:38+03:00" stEvt:softwareAgent="Adobe Photoshop 21M .0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:13281450-52c7-4240-b6a3-62ce81f0d62a" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sTyM pe/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:01:22+03:00" xmp:ModifyDate="2023-02-03T00:01:22+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:8e2a4b23-920e-f44f-a855-cf46bece9459" xmpMM:DocumentID="adobe:docid:photoshop:c767911c-8382-a24a-b670-8e6d1440beab" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshopM :ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:49f32109-a10M e-e941-b5e4-2cfa757792b3" stEvt:when="2023-02-03T00:01:22+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:8e2a4b23-920e-f44f-a855-cf46bece9459" stEvt:when="2023-02-03T00:01:22+03:00" stEvt:softwareAgent="Adobe PhotoM shop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:49f32109-a10e-e941-b5e4-2cfa757792b3" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sM Type/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:57:44+03:00" xmp:ModifyDate="2023-02-02T23:57:44+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:b85c81db-79a0-b745-877e-bc8bb1059680" xmpMM:DocumentID="adobe:docid:photoshop:563cdac9-6491-f344-9990-e3588ba9f4b3" xmpMM:OriginalDocumentID="xmp.did:8653deM 18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rM df:li stEvt:action="saved" stEvt:instanceID="xmp.iid:430c7380-9e80-b343-9410-efcb0efa7eeb" stEvt:when="2023-02-02T23:57:44+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:b85c81db-79a0-b745-877e-bc8bb1059680" stEvt:M when="2023-02-02T23:57:44+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:430c7380-9e80-b343-9410-efcb0efa7eeb" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> M </rdf:RDF> </x:xmpmeta> M M M <?xpackeM Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Mined by AntPool958[ Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobM e.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:53:30+03:00" xmp:ModifyDate="2023-02-02T23:53:30+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:7cf43313M -f245-4c40-b5e8-23257f11c382" xmpMM:DocumentID="adobe:docid:photoshop:d5a23159-0ab2-da46-958f-414525789fdc" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4M a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:92ce1660-b9b8-2146-b9a9-4c74905dda48" stEvt:when="2023-02-02T23:53:30+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from M application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:7cf43313-f245-4c40-b5e8-23257f11c382" stEvt:when="2023-02-02T23:53:30+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:92ce1660-b9b8-2146-b9a9-4c74905dda48" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <phM otoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ce Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmM lns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:55:05+03:00" xmp:ModifyDate="2023-02-02T23:55:05+03:00" dc:format="image/jpeg" xmpMM:InstaM nceID="xmp.iid:307af74e-1fff-5d45-aa88-2c82ec0f80fe" xmpMM:DocumentID="adobe:docid:photoshop:52014a0f-adcf-0044-93b3-2329f0f45e6f" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:iM nstanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:250bf60e-1e54-1d43-88fe-3c0461ee951a" stEvt:when="2023-02-02T23:55:05+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:paraM meters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:307af74e-1fff-5d45-aa88-2c82ec0f80fe" stEvt:when="2023-02-02T23:55:05+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:250bf60e-1e54-1d43-88fe-3c0461ee951a" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-aM c05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/eleM ments/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:51:47+03:00" xmp:ModifyDate="2023-02-02T23:51:47+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:2d284bd5-7419-e64f-b850-51c728a0b5b1" xmpMM:DocumentID="M adobe:docid:photoshop:c9307d57-c715-524c-a3f7-5e3961863ef9" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="20M 23-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:4177826a-1b3c-d24f-8ce6-8be982cee24b" stEvt:when="2023-02-02T23:51:47+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/>M <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:2d284bd5-7419-e64f-b850-51c728a0b5b1" stEvt:when="2023-02-02T23:51:47+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:4177826a-1b3c-d24f-8ce6-8be982cee24b" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CM D41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 Reference Viewing Condition in IEC61966-2.1 c/Foundry USA Pool #dropgold/! KjI=:BNB.BUSD-BD1:bnb1c667cxycg4cgjt7ae4c3cgj9agtmzdrnw3la90:1360015365:te:0 FjDOUT:8513733D2F3272B455D05FF4BE690759B1F0AF75F56E14030F66376280694301 FjDOUT:88C70FFF989C930F94E82BF24F2933FA4EE87B5DD7156D7673AE9FB9AC321FE6 iTXtXML:com.adobe.xmp <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 6.0.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:GIMP="http://www.giM xmlns:tiff="http://ns.adobe.com/tiff/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:exif="http://ns.adobe.com/exif/1.0/"> <xmp:CreatorTool>GIMP 2.10.32</xmp:CreatorTool> <xmp:MetadataDate>2023:02:02T22:29:40-05:00</xmp:MetadataDate> <xmp:ModifyDate>2023-02-02T22:29:40</xmp:ModifyDate> <GIMP:Version>2.10.32</GIMP:Version> <GIMP:Platform>Mac OS</GIMP:Platform> <GIMP:TimeStamp>1675394983316255</GIMP:TimeStamp> <tiff:ResolutionUnit>3</tiff:ResolutionUnit> <tiff:Orientation>1</tiff:Orientation> <tiff:YResolution>300</tiff:YResolution> <tiff:XResolution>300</tiff:XResolution> <dc:description> <rdf:Alt> <rdf:li xml:lang="x-default">Created with GIMP</rdf:li> </rdf:Alt> </dc:description> <dc:Format>image/png</dc:Format> <xmpMM:History> <rdf:Seq> <rdf:li rdf:parseType="Resource"> <stEvt:changed>/</stEvt:changed> <stEvt:softwareAgent>Gimp 2.10 (Mac OS)</stEvt:softwareAgent> <stEvt:when>2023-02-02T22:29:43-05:00</stEvt:when> <stEvt:instanceID>xmp.iid:3bd39d47-e1d5-4912-a1b9-3080716bbd88</stEvt:instanceID> <stEvt:action>saved</stEvt:action> </rdf:Seq> </xmpMM:History> <xmpMM:OriginalDocumentID>xmp.did:26bdec4f-7856-442b-be21-d59d7eeeb323</xmpMM:OriginalDocumentID> <xmpMM:InstanceID>xmp.iid:36cbb0fe-daf7-4304-873e-cc0175b2fb5b</xmpMM:InstanceID> <xmpMM:DocumentID>gimp:docid:gimp:b87b518a-f04b-4ba0-b6b1-071cfa9e0cd4</xmpMM:DocumentID> <exif:ColorSpace>1</exif:ColorSpace> <exif:UserComment> <rdf:Alt> <rdf:li xml:lang="x-default">Created with GIMPM </rdf:Alt> </exif:UserComment> </rdf:Description> text/html;charset=utf-8 <!DOCTYPE html><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>FREEDOM TO TRANSACT</title><script src="https://hv4gxzchk24cqfezebn3ujjz6oy2kbtztv5vghn6kpbkjc3vg4rq.arweave.net/LwoYxA0u8husyQUnezc7A5x4EU-rzYNfLnHtICXCBzc"></script><script src="data:text/javascript;base64,dmFyIGxvZ287CnZhciBiZzsKdmFyIHggPSAwOwp2YXIgeSA9IDA7CnZhciBzc3BlZWQgPSAzCnZhciB4ZGlyID0gMTsKdmFyIHlkaXIgPSAxOwp2YXIgZmxpcHNwZWVkID0gMC4wMQoKZnVuY3Rpb24gcHJlbG9hZCgpIHsKICBsb2dvID0gbG9hZEltYWdlKCJodHRwczovL3g0ZDVM hNXlxNDRxd29mZDJlbzZoZHpwaG5qZG51bTU0NG1mNnJ5dmV0Y2htYndpcTR5d2EuYXJ3ZWF2ZS5uZXQvdndmUWR4RG5JV2NVZWlPOGNlWG5ha2JhTTd6akMtamlwSmlPd05rUTVpdyIpOwogIGJnID0gbG9hZEltYWdlKCJodHRwczovL2Fyd2VhdmUubmV0L2xZVUZXLXZSVU5ZWkMwTWx3NDVfdXVhXzlLekNGNHZ3RTMtYVJVLTVTVkkiKTsKICBzcGVlZCA9IHJhbmRvbSgxLDUpOwogIGZsaXBzcGVlZCA9IGZsaXBzcGVlZCAqIHJhbmRvbSgyLDgpOwp9CgpmdW5jdGlvbiBzZXR1cCgpIHsKICBjcmVhdGVDYW52YXMoNjQ4LCA5NDksIFdFQkdMKTsKfQoKZnVuY3Rpb24gZHJhdygpIHsKICBiYWNrZ3JvdW5kKDIwMCk7CiAgaW1hZ2UoYmcsIC0zMjQsIC00NzQsIHdpZHRoLCBoZWlnaHQpOyAM gCiAgbm9TdHJva2UoKTsKICAKICB4ID0geCArIHNzcGVlZCAqIHhkaXI7CiAgeSA9IHkgKyBzc3BlZWQgKiB5ZGlyOwogIAogIGlmICh4ID4gMzI0IC0gMjQwIHx8IHggPCAtMzI0LSAxMjUpIHsKICAgIHhkaXIgPSAteGRpcjsKICB9CiAgaWYgKHkgPiA0NzQgLTQwIHx8IHkgPCAtNDc0KyAxMDApIHsKICAgIHlkaXIgPSAteWRpcjsKICB9CiAgCiAgcHVzaCgpOwogIHRyYW5zbGF0ZSh4LCB5LCA2NCk7CiAgcm90YXRlWChmcmFtZUNvdW50ICogZmxpcHNwZWVkKTsKICB0ZXh0dXJlKGxvZ28pOwogIGNpcmNsZSgyMDAsIDEsIDEyMCk7CiAgcG9wKCk7Cn0"></script></head><body></body></html> Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://M ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:52:33+03:00" xmp:ModifyDate="2023-02-02T23:52:33+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:5414536d-98dc-9649-8e56-525aa9029c54" xmpMM:DocumentID="adobe:docid:photoshop:704f48c4-ad26-a34d-a33a-13d845c5315f" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshopM :ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceM ID="xmp.iid:d1d451fe-ef31-0b4e-a892-a7d7e3c90329" stEvt:when="2023-02-02T23:52:33+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:5414536d-98dc-9649-8e56-525aa9029c54" stEvt:when="2023-02-02T23:52:33+03:00" stEvt:soM ftwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:d1d451fe-ef31-0b4e-a892-a7d7e3c90329" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ hiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/phM otoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-02T23:56:56+03:00" xmp:ModifyDate="2023-02-02T23:56:56+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:25e78c1e-f867-b94b-a5c5-1ff5349d788e" xmpMM:DocumentID="adobe:docid:photoshop:4c00ebc6-2c85-1044-9569-547ce0f7eefe" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:SeM q> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:4512ea08-c005-fa41-b551-22f0a19d8fe5" stEvt:when="2023-02-02T23:56:5M 6+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:25e78c1e-f867-b94b-a5c5-1ff5349d788e" stEvt:when="2023-02-02T23:56:56+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmM pMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:4512ea08-c005-fa41-b551-22f0a19d8fe5" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 FjD=:THOR.RUNE:thor1pwdwh5x2080epw989x6wmh2e2yj3f2ch7vw9pa:1361320035:t' #%'+-/357:<>BDFHLNPTUW[]_cegkmortvz|~ LAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU ULAME3.100UUUUUUUUUUUUM UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU| LAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU LAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU NhELAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUh 0}LAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUXu 4ALAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUH 100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU LAME3.100UUUUUUUUUUUUUU ULAME3.100UUUUUUUUUUUUUUU ULAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU LAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU sLAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUM UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU LAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUM UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUM UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUULAME3.100UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUh! 0G#UUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU0 UUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUY(i UUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUM ~UUUUUUUUUUUUUUUUUUU rqUUUUUUUUUUUUUUUUUU JrqUUUUUUUUUUUUUUUUUU ff\rqUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUU E3.98UUUUUUUUUUUUUUU rqUUUUUUUUUUUUUUUUUU \rqUUUUUUUUUUUUUUUUU 82L"UUUUUUUUUUUUUUUUUUUU CUUUUUUUUUUUUUUUUUUUU f\rqUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU: GqUUUUUUUUUUUUUUUUUUUU KrUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUnY# f\rqUUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUR UUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU KLAME3.98UUUUUUUUUUUUM GrUUUUUUUUUUUUUUUUUUU GYUUUUUUUUUUUUUUUUUUUU rH UUUUUUUUUUUUUUUUUUUUUUUUUUU 9UUUUUUUUUUUUUUUUUUUv f\rqUUUUUUUUUUUUUUUUUUUUUU VUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU". TUUUUUUUUUUUUUUUUUUU SqUUUUUUUUUUUUUUUUUUUU VUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU [UUUUUUUUUUUUUUUUUUUU LAME3.98UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU GrUUUUUUUUUUUUUUUUUUU SsUUUUUUUUUUUUUUUUUUUU f\rqUUUUUUUUUUUUUUUUUUU XUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUU MmUUUUUUUUUUUUUUUUUUUU QQUUUUUUUUUUUUUUUUUUU MTUUUUUUUUUUUUUUUUUUU f\rqUUUUUUUUUUUUUUUUU SwUUUUUUUUUUUUUUUUUUU 8UUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU Ms8UUUUUUUUUUUUUUUUUUUo MvUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUUU ZUUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU UUUUUUUUUUUUUUUUUUUU -=-157:::#+?D?8C49:7 %77777777777777777777777777777777777777777777777777 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntaM x-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T00:07:10+03:00" xmp:ModifyDate="2023-02-03T00:07:10+0M 3:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:c1285253-4a7e-7649-9feb-5d382bb5fdfe" xmpMM:DocumentID="adobe:docid:photoshop:272eaaaa-b262-1b41-9821-a308bee336dc" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)M "/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:d1bb25e2-91e2-ea45-b185-aa072cd39ecc" stEvt:when="2023-02-03T00:07:10+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <M rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:c1285253-4a7e-7649-9feb-5d382bb5fdfe" stEvt:when="2023-02-03T00:07:10+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:d1bb25e2-91e2-ea45-b185-aa072cd39ecc" stRef:documentID="adobe:docid:photoshop:6564fe94-253e-b74d-87e1-5aecb72f57e0" stRef:originalM DocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RM GB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 -=-157:::#+?D?8C49:7 %77777777777777777777777777777777777777777777777777 ************************************************** %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz &'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz c/Foundry USA Pool #dropgold/ 7j5+:BTC.BTC:thor1pwdwh5x2080epw989x6wmh2e2yj3f2ch7vw9pa :http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="XMP Core 5.5.0"> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" photoshop:ColorMode="3" photoshop:ICCProfile="M Display P3" xmp:ModifyDate="2023-02-02T22:41:55-05:00" xmp:MetadataDate="2023-02-02T22:41:55-05:00"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="produced" stEvt:softwareAgent="Affinity Photo 1.10.6" stEvt:when="2023-02-02T22:41:55-05:00"/> </rdf:Seq> </xmpMM:History> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> (B+(%%(Q:=0B`Ued_U][jx Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.comM /xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:38:18+03:00" xmp:ModifyDate="2023-02-03T06:38:18+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:866271e1-27f4-a14b-988a-23da8ca1ee00" xmpMM:DocumentID="adobe:docid:photoshop:bb95b76a-271d-1440-b7db-770a5bc441e6" xmpMM:OriginalDocumentID="xmp.M did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changeM d="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:760b500e-ae25-3a4a-a92e-3e1a3b0983ce" stEvt:when="2023-02-03T06:38:18+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:866271e1-27f4-a14b-988a-23da8ca1eeM 00" stEvt:when="2023-02-03T06:38:18+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:760b500e-ae25-3a4a-a92e-3e1a3b0983ce" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:DesM cription> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.M com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:38:53+03:00" xmp:ModifyDate="2023-02-03T06:38:53+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:29b0be4c-5M d64-9040-b0ac-f8362c9e6c3c" xmpMM:DocumentID="adobe:docid:photoshop:0c413839-96f8-684a-bf3e-881dc95d1a18" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a4M 96-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:717a847b-8139-174b-a4bf-a630b355c9bc" stEvt:when="2023-02-03T06:38:53+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from apM plication/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:29b0be4c-5d64-9040-b0ac-f8362c9e6c3c" stEvt:when="2023-02-03T06:38:53+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:717a847b-8139-174b-a4bf-a630b355c9bc" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photM oshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> opyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmetM a xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorToolM ="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:35:47+03:00" xmp:ModifyDate="2023-02-03T06:35:47+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:1b892532-d4ac-ee42-be47-b3b86a85c825" xmpMM:DocumentID="adobe:docid:photoshop:dd536a55-2f9a-0744-8d01-5bc101df1997" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="creaM ted" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:506a3fb5-7807-5940-946c-8a7ac096fcd6" stEvt:when="2023-02-03T06:35:47+03:00" stEvt:softwareAgent=M "Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:1b892532-d4ac-ee42-be47-b3b86a85c825" stEvt:when="2023-02-03T06:35:47+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrM om stRef:instanceID="xmp.iid:506a3fb5-7807-5940-946c-8a7ac096fcd6" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobeM :ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe PhotoshoM p 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:38:34+03:00" xmp:ModifyDate="2023-02-03T06:38:34+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:78f6a023-58b0-4842-871a-99fcda45817a" xmpMM:DocumentID="adobe:docid:photoshop:231e4c4e-c199-774d-9d59-a74075502ec1" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instaM nceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:ccf585da-b06b-f443-9a31-0d1fcbd5336e" stEvt:when="2023-02-03T06:38:34+03:00" stEvt:softwareAgent="Adobe PhotoshopM 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:78f6a023-58b0-4842-871a-99fcda45817a" stEvt:when="2023-02-03T06:38:34+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instancM eID="xmp.iid:ccf585da-b06b-f443-9a31-0d1fcbd5336e" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 -=-157:::#+?D?8C49:7 %77777777777777777777777777777777777777777777777777 Z&jINE.E.E.E.E.E.E.E- Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.M com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:39:29+03:00" xmp:ModifyDate="2023-02-03T06:39:29+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:2045d1b8-798a-2344-8fba-e9d305342676" xmpMM:DocumentID="adobe:docid:photoshop:5e457855-5097-184c-9e55-007b22260f54" xmpMM:OriginalDocumentID="xM mp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:chaM nged="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:f335aa63-54ed-494c-b3c3-e998fd960e11" stEvt:when="2023-02-03T06:39:29+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:2045d1b8-798a-2344-8fba-e9d3053M 42676" stEvt:when="2023-02-03T06:39:29+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:f335aa63-54ed-494c-b3c3-e998fd960e11" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:M Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="htM tp://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:36:48+03:00" xmp:ModifyDate="2023-02-03T06:36:48+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:2776fc6c-3e74-0642-a805-e69a7329556a" xmpMM:DocumentID="adobe:docid:photoshop:9e197M 735-69cc-8844-abe4-da716c3a4837" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stM Evt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:96e91e6c-2ed6-4146-bb92-c47869790001" stEvt:when="2023-02-03T06:36:48+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saveM d" stEvt:instanceID="xmp.iid:2776fc6c-3e74-0642-a805-e69a7329556a" stEvt:when="2023-02-03T06:36:48+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:96e91e6c-2ed6-4146-bb92-c47869790001" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733M F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing ConditionM Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/RM esourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:37:03+03:00" xmp:ModifyDate="2023-02-03T06:37:03+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:4cbe6b62-1a68-5d40-bfdc-82f8b7d94068" xmpMM:DocumentID="adobe:docid:photoshop:b388ec5b-eb0b-b843-886f-041d50c9a32b" xmpMM:OriginalDocumentID="xmp.did:8653de18-59bM 9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li M stEvt:action="saved" stEvt:instanceID="xmp.iid:3a64df4c-03cb-d84b-9cd0-b6bfd0e4201c" stEvt:when="2023-02-03T06:37:03+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:4cbe6b62-1a68-5d40-bfdc-82f8b7d94068" stEvt:when="M 2023-02-03T06:37:03+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:3a64df4c-03cb-d84b-9cd0-b6bfd0e4201c" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:M RDF> </x:xmpmeta> M M M <?xpacket end=M Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adoM be.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:37:44+03:00" xmp:ModifyDate="2023-02-03T06:37:44+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:f44efd61-97b8-aa41-a68f-736ce5dd0b0a" xmpMM:DocumentID="adobe:docid:photoshop:5f852445-801b-0aM 41-b8b7-9b826c66b132" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwarM eAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:b00dc16f-2967-014d-ae95-4ceb6cb02a74" stEvt:when="2023-02-03T06:37:44+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:inM stanceID="xmp.iid:f44efd61-97b8-aa41-a68f-736ce5dd0b0a" stEvt:when="2023-02-03T06:37:44+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:b00dc16f-2967-014d-ae95-4ceb6cb02a74" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li>M </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC6196M Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/M " x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (WM indows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:37:23+03:00" xmp:ModifyDate="2023-02-03T06:37:23+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:971b94ce-8c57-214b-800d-ff6aa998e5a5" xmpMM:DocumentID="adobe:docid:photoshop:9706f5b8-be84-d344-9302-619d0ab33de9" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmM p.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:ea0d8694-4b95-2740-8ab7-2810937c31ea" stEvt:when="2023-02-03T06:37:23+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (WiM ndows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:971b94ce-8c57-214b-800d-ff6aa998e5a5" stEvt:when="2023-02-03T06:37:23+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.M iid:ea0d8694-4b95-2740-8ab7-2810937c31ea" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 text/plain;charset=utf-8 ############################################# ### Announcing the Rare Ordinal Directory ### # # # Directory: http://rareordinal.directory # # Telegram: https://t.me/+qIdmbUk_m6ZmZTgx # # Twitter: https://twitter.com/RareOrdinals # # # # Submit Here Your Rare Ordinals Today # # -> http://rareordinal.directory/submit <- # #############################################h! Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elM ements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:40:24+03:00" xmp:ModifyDate="2023-02-03T06:40:24+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:2123933d-1492-b04e-b8c1-af1b6c6fa0b8" xmpMM:DocumentID=M "adobe:docid:photoshop:a86a9f28-8a98-7241-a0f5-81290e93c2f2" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2M 023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:703298d2-9559-e049-9c4b-c86c1fdf2de8" stEvt:when="2023-02-03T06:40:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/M > <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:2123933d-1492-b04e-b8c1-af1b6c6fa0b8" stEvt:when="2023-02-03T06:40:24+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:703298d2-9559-e049-9c4b-c86c1fdf2de8" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72M CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036M , 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26M +03:00" xmp:MetadataDate="2023-02-03T06:39:45+03:00" xmp:ModifyDate="2023-02-03T06:39:45+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:c9e09a4f-a3a2-3c4e-8056-daf7978965a7" xmpMM:DocumentID="adobe:docid:photoshop:fe1a9504-ddfa-3043-8cca-73409949309d" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" M stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:49f1d22a-4050-8e4c-9e1c-3b46091b904c" stEvt:when="2023-02-03T06:39:45+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:acM tion="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:c9e09a4f-a3a2-3c4e-8056-daf7978965a7" stEvt:when="2023-02-03T06:39:45+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:49f1d22a-4050-8e4c-9e1c-3b46091b904c" stM Ref:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool ://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/M sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:42:14+03:00" xmp:ModifyDate="2023-02-03T06:42:14+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:2a285d8e-af91-7843-9eb0-13c3ff3d7e77" xmpMM:DocumentID="adobe:docid:photoshop:114882fe-22d3-6040-a7ae-b378e1deead5" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photosM hop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:08dbc7d2-M d9f3-fc4e-99d3-82f2b7cf9940" stEvt:when="2023-02-03T06:42:14+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:2a285d8e-af91-7843-9eb0-13c3ff3d7e77" stEvt:when="2023-02-03T06:42:14+03:00" stEvt:softwareAgent="Adobe PhM otoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:08dbc7d2-d9f3-fc4e-99d3-82f2b7cf9940" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://puM rl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:42:37+03:00" xmp:ModifyDate="2023-02-03T06:42:37+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:ad95f6f8-f0e9-334b-9933-129e37534ce9" xmpMMM :DocumentID="adobe:docid:photoshop:115c38c6-7a83-8949-967f-dd39db2b9693" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" sM tEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:967396ea-3204-c64c-aa3b-d24c47b61a89" stEvt:when="2023-02-03T06:42:37+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to M image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:ad95f6f8-f0e9-334b-9933-129e37534ce9" stEvt:when="2023-02-03T06:42:37+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:967396ea-3204-c64c-aa3b-d24c47b61a89" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:BagM > <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-PackardM IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.M ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlM ns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:40:09+03:00" xmp:ModifyDate="2023-02-03T06:40:09+03:00" dc:format="image/jpeg" xmpMM:InstanM ceID="xmp.iid:1a30e2b8-7d9b-0841-adc8-10c52a07a582" xmpMM:DocumentID="adobe:docid:photoshop:ee195b12-b90c-204f-bc07-e7b21a7c4d7d" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:inM stanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:8f12ea3d-f6f6-3c40-9e19-d92fe2ee1bba" stEvt:when="2023-02-03T06:40:09+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:paramM eters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:1a30e2b8-7d9b-0841-adc8-10c52a07a582" stEvt:when="2023-02-03T06:40:09+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:8f12ea3d-f6f6-3c40-9e19-d92fe2ee1bba" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-acM 05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ ="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adM obe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:39:58+03:00" xmp:ModifyDate="2023-02-03T06:39:58+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:24dfa88b-3169-4949-98d4-e83bc4914377" xmpMM:DocumentID="adobe:docid:photoshop:90ad8cde-dfce-3e44-8663-ecaaf25bcbe5" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:HistorM y> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:5789b27e-df10-bb4b-958b-a123971e56bb" stEvt:when="2023-02-M 03T06:39:58+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:24dfa88b-3169-4949-98d4-e83bc4914377" stEvt:when="2023-02-03T06:39:58+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdfM :Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:5789b27e-df10-bb4b-958b-a123971e56bb" stRef:documentID="adobe:docid:photoshop:3eda5629-51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour space - sRGB .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 Adobe Photoshop 21.0 (Windows) cropWhenPrintingbool http://ns.adobe.com/xap/1.0/ " id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.6-c148 79.164036, 2019/08/13-01:06:57 "> <rdf:RDF xmlns:M rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stEvt="http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmlns:photoshop="http://ns.adobe.com/photoshop/1.0/" xmp:CreatorTool="Adobe Photoshop 21.0 (Windows)" xmp:CreateDate="2023-01-31T19:01:26+03:00" xmp:MetadataDate="2023-02-03T06:41:13+0M 3:00" xmp:ModifyDate="2023-02-03T06:41:13+03:00" dc:format="image/jpeg" xmpMM:InstanceID="xmp.iid:d5e466fc-49f2-6548-abb5-9d3996892f57" xmpMM:DocumentID="adobe:docid:photoshop:6cd5ae32-a44c-6d4b-83a4-0bcc36d3818c" xmpMM:OriginalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288" photoshop:ColorMode="3" photoshop:ICCProfile="sRGB IEC61966-2.1"> <xmpMM:History> <rdf:Seq> <rdf:li stEvt:action="created" stEvt:instanceID="xmp.iid:8653de18-59b9-7643-ac05-7a8bf5110288" stEvt:when="2023-01-31T19:01:26+03:00" stEvt:soM ftwareAgent="Adobe Photoshop 21.0 (Windows)"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:3be4a496-9a2e-5b43-b9e4-aa2e09192e28" stEvt:when="2023-01-31T21:56:39+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:815468ae-7084-5b49-b30b-518083629483" stEvt:when="2023-02-03T06:41:13+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> <rdf:li stEvt:action="converted" stEvt:parameters="from applicaM tion/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="derived" stEvt:parameters="converted from application/vnd.adobe.photoshop to image/jpeg"/> <rdf:li stEvt:action="saved" stEvt:instanceID="xmp.iid:d5e466fc-49f2-6548-abb5-9d3996892f57" stEvt:when="2023-02-03T06:41:13+03:00" stEvt:softwareAgent="Adobe Photoshop 21.0 (Windows)" stEvt:changed="/"/> </rdf:Seq> </xmpMM:History> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:815468ae-7084-5b49-b30b-518083629483" stRef:documentID="adobe:docid:photoshop:3eda5629-M 51f1-de48-8662-170ca4a0cd94" stRef:originalDocumentID="xmp.did:8653de18-59b9-7643-ac05-7a8bf5110288"/> <photoshop:DocumentAncestors> <rdf:Bag> <rdf:li>72CD41DEE1DA26104D97BB46D1E733F2</rdf:li> </rdf:Bag> </photoshop:DocumentAncestors> </rdf:Description> </rdf:RDF> </x:xmpmeta> M M M M <?xpacket end="w"?> Copyright (c) 1998 Hewlett-Packard Company IEC http://www.iec.ch IEC http://www.iec.ch .IEC 61966-2.1 Default RGB colour spacM .IEC 61966-2.1 Default RGB colour space - sRGB ,Reference Viewing Condition in IEC61966-2.1 ,Reference Viewing Condition in IEC61966-2.1 6j4ion:2.QmSfiXBHE2WetDMpKmrxfBFLWAG3bmruVRW8SmsQeVbpWYZF ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, /ViaBTC/Mined by hashcloud/, ************************************************** %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz &'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz I=5L<,L@>UG5UIAW;.X/ c/Foundry USA Pool #dropgold/ Mined by AntPool960J 6j4ion:3.QmNNZiYAdMPtadhME8JqEHKc5nNJfPjWxdZn28uRY64f2vq/ 584c631b1bdb70775b6ffd56653428c8H0E 8686c0d1d13b60f308178ba8bb3eaf34H0E 203c6da29c7649e5faea624a1aa0ea92H0E c/Foundry USA Pool #dropgold/ 7j5+:BTC.BTC:thor1h8txnahwctgw96ryl3ahlwk27f7a0gpz225st2I 4j2DC-L5:i86rV/UdWykSV3X+9Xjyz91wpdJyrc+9+T4VdSEw2Qg= FjDOUT:15CB61C6180E0BF7EB0489A98CF70DEBFAA6187828D539D2B89A94E2CA8F5B62 Bj@=:ETH.ETH:0x5BE75Eb25fd73adD2696BF8Db3Cd25e83388FBb3:782207:te:0 Bj@=:BNB.BNB:bnb16qnuncx9p79c2dpylmqpjzjqsyn3du3wpuq0hk:617090:te:0 EjC=:BNB.BNB:bnb1rjf4z8wnzz6p2ahxl7azhluhtrf62dx88mqtkp:141978589:te:0 c/Foundry USA Pool #dropgold/ FjDOUT:BB19409EE48BE060BF5A6AF12BCA71302BBBA6A5277B32BC00DE072676DF6CE1 FjDOUT:8321CA108AA1AE8E03AF5E6CAF647B251CBE2EA70D85B46CCD4FF6DBA75B04AB FjDOUT:7C20ED9EDD090AF18A8B7913853679D5C1F5869772F39AF99CDAF9924E15D304 FjDOUT:27959554207220531846022250D4E4FF8579BF1415B348C5BC786895F3568C6A FjDOUT:A0C8C80E18C4FB68A3DB574A36C9BA00E890416F4B9ECC8C54D4D0D3431857A1 FjDOUT:134C324E9C5E7EBB6E27AF5ABA35E3B3A3BFFA43D9A67A67C664BDEF59A96A61 c/Foundry USA Pool #dropgold/!
blk00000.txt blk00001.txt blk00002.txt blk00003.txt blk00004.txt blk00005.txt blk00006.txt blk00007.txt blk00008.txt blk00009.txt blk00010.txt blk00011.txt blk00012.txt blk00013.txt blk00014.txt blk00015.txt blk00016.txt blk00017.txt blk00018.txt blk00019.txt blk00020.txt blk00021.txt blk00022.txt blk00023.txt blk00024.txt blk00025.txt blk00026.txt blk00027.txt blk00028.txt blk00029.txt blk00030.txt blk00031.txt blk00032.txt blk00033.txt blk00034.txt blk00035.txt blk00036.txt blk00037.txt blk00038.txt blk00039.txt blk00040.txt blk00041.txt blk00042.txt blk00043.txt blk00044.txt blk00045.txt blk00046.txt blk00047.txt blk00048.txt blk00049.txt blk00050.txt blk00051.txt blk00052.txt blk00053.txt blk00054.txt blk00055.txt blk00056.txt blk00057.txt blk00058.txt blk00059.txt blk00060.txt blk00061.txt blk00062.txt blk00063.txt blk00064.txt blk00065.txt blk00066.txt blk00067.txt blk00068.txt blk00069.txt blk00070.txt blk00071.txt blk00072.txt blk00073.txt blk00074.txt blk00075.txt blk00076.txt blk00077.txt blk00078.txt blk00079.txt blk00080.txt blk00081.txt blk00082.txt blk00083.txt blk00084.txt blk00085.txt blk00086.txt blk00087.txt blk00088.txt blk00089.txt blk00090.txt blk00091.txt blk00092.txt blk00093.txt blk00094.txt blk00095.txt blk00096.txt blk00097.txt blk00098.txt blk00099.txt blk00100.txt blk00101.txt blk00102.txt blk00103.txt blk00104.txt blk00105.txt blk00106.txt blk00107.txt blk00108.txt blk00109.txt blk00110.txt blk00111.txt blk00112.txt blk00113.txt blk00114.txt blk00115.txt blk00116.txt blk00117.txt blk00118.txt blk00119.txt blk00120.txt blk00121.txt blk00122.txt blk00123.txt blk00124.txt blk00125.txt blk00126.txt blk00127.txt blk00128.txt blk00129.txt blk00130.txt blk00131.txt blk00132.txt blk00133.txt blk00134.txt blk00135.txt blk00136.txt blk00137.txt blk00138.txt blk00139.txt blk00140.txt blk00141.txt blk00142.txt blk00143.txt blk00144.txt blk00145.txt blk00146.txt blk00147.txt blk00148.txt blk00149.txt blk00150.txt blk00151.txt blk00152.txt blk00153.txt blk00154.txt blk00155.txt blk00156.txt blk00157.txt blk00158.txt blk00159.txt blk00160.txt blk00161.txt blk00162.txt blk00163.txt blk00164.txt blk00165.txt blk00166.txt blk00167.txt blk00168.txt blk00169.txt blk00170.txt blk00171.txt blk00172.txt blk00173.txt blk00174.txt blk00175.txt blk00176.txt blk00177.txt blk00178.txt blk00179.txt blk00180.txt blk00181.txt blk00182.txt blk00183.txt blk00184.txt blk00185.txt blk00186.txt blk00187.txt blk00188.txt blk00189.txt blk00190.txt blk00191.txt blk00192.txt blk00193.txt blk00194.txt blk00195.txt blk00196.txt blk00197.txt blk00198.txt blk00199.txt blk00200.txt blk00201.txt blk00202.txt blk00203.txt blk00204.txt blk00205.txt blk00206.txt blk00207.txt blk00208.txt blk00209.txt blk00210.txt blk00211.txt blk00212.txt blk00213.txt blk00214.txt blk00215.txt blk00216.txt blk00217.txt blk00218.txt blk00219.txt blk00220.txt blk00221.txt blk00222.txt blk00223.txt blk00224.txt blk00225.txt blk00226.txt blk00227.txt blk00228.txt blk00229.txt blk00230.txt blk00231.txt blk00232.txt blk00233.txt blk00234.txt blk00235.txt blk00236.txt blk00237.txt blk00238.txt blk00239.txt blk00240.txt blk00241.txt blk00242.txt blk00243.txt blk00244.txt blk00245.txt blk00246.txt blk00247.txt blk00248.txt blk00249.txt blk00250.txt blk00251.txt blk00252.txt blk00253.txt blk00254.txt blk00255.txt blk00256.txt blk00257.txt blk00258.txt blk00259.txt blk00260.txt blk00261.txt blk00262.txt blk00263.txt blk00264.txt blk00265.txt blk00266.txt blk00267.txt blk00268.txt blk00269.txt blk00270.txt blk00271.txt blk00272.txt blk00273.txt blk00274.txt blk00275.txt blk00276.txt blk00277.txt blk00278.txt blk00279.txt blk00280.txt blk00281.txt blk00282.txt blk00283.txt blk00284.txt blk00285.txt blk00286.txt blk00287.txt blk00288.txt blk00289.txt blk00290.txt blk00291.txt blk00292.txt blk00293.txt blk00294.txt blk00295.txt blk00296.txt blk00297.txt blk00298.txt blk00299.txt blk00300.txt blk00301.txt blk00302.txt blk00303.txt blk00304.txt blk00305.txt blk00306.txt blk00307.txt blk00308.txt blk00309.txt blk00310.txt blk00311.txt blk00312.txt blk00313.txt blk00314.txt blk00315.txt blk00316.txt blk00317.txt blk00318.txt blk00319.txt blk00320.txt blk00321.txt blk00322.txt blk00323.txt blk00324.txt blk00325.txt blk00326.txt blk00327.txt blk00328.txt blk00329.txt blk00330.txt blk00331.txt blk00332.txt blk00333.txt blk00334.txt blk00335.txt blk00336.txt blk00337.txt blk00338.txt blk00339.txt blk00340.txt blk00341.txt blk00342.txt blk00343.txt blk00344.txt blk00345.txt blk00346.txt blk00347.txt blk00348.txt blk00349.txt blk00350.txt blk00351.txt blk00352.txt blk00353.txt blk00354.txt blk00355.txt blk00356.txt blk00357.txt blk00358.txt blk00359.txt blk00360.txt blk00361.txt blk00362.txt blk00363.txt blk00364.txt blk00365.txt blk00366.txt blk00367.txt blk00368.txt blk00369.txt blk00370.txt blk00371.txt blk00372.txt blk00373.txt blk00374.txt blk00375.txt blk00376.txt blk00377.txt blk00378.txt blk00379.txt blk00380.txt blk00381.txt blk00382.txt blk00383.txt blk00384.txt blk00385.txt blk00386.txt blk00387.txt blk00388.txt blk00389.txt blk00390.txt blk00391.txt blk00392.txt blk00393.txt blk00394.txt blk00395.txt blk00396.txt blk00397.txt blk00398.txt blk00399.txt blk00400.txt blk00401.txt blk00402.txt blk00403.txt blk00404.txt blk00405.txt blk00406.txt blk00407.txt blk00408.txt blk00409.txt blk00410.txt blk00411.txt blk00412.txt blk00413.txt blk00414.txt blk00415.txt blk00416.txt blk00417.txt blk00418.txt blk00419.txt blk00420.txt blk00421.txt blk00422.txt blk00423.txt blk00424.txt blk00425.txt blk00426.txt blk00427.txt blk00428.txt blk00429.txt blk00430.txt blk00431.txt blk00432.txt blk00433.txt blk00434.txt blk00435.txt blk00436.txt blk00437.txt blk00438.txt blk00439.txt blk00440.txt blk00441.txt blk00442.txt blk00443.txt blk00444.txt blk00445.txt blk00446.txt blk00447.txt blk00448.txt blk00449.txt blk00450.txt blk00451.txt blk00452.txt blk00453.txt blk00454.txt blk00455.txt blk00456.txt blk00457.txt blk00458.txt blk00459.txt blk00460.txt blk00461.txt blk00462.txt blk00463.txt blk00464.txt blk00465.txt blk00466.txt blk00467.txt blk00468.txt blk00469.txt blk00470.txt blk00471.txt blk00472.txt blk00473.txt blk00474.txt blk00475.txt blk00476.txt blk00477.txt blk00478.txt blk00479.txt blk00480.txt blk00481.txt blk00482.txt blk00483.txt blk00484.txt blk00485.txt blk00486.txt blk00487.txt blk00488.txt blk00489.txt blk00490.txt blk00491.txt blk00492.txt blk00493.txt blk00494.txt blk00495.txt blk00496.txt blk00497.txt blk00498.txt blk00499.txt blk00500.txt blk00501.txt blk00502.txt blk00503.txt blk00504.txt blk00505.txt blk00506.txt blk00507.txt blk00508.txt blk00509.txt blk00510.txt blk00511.txt blk00512.txt blk00513.txt blk00514.txt blk00515.txt blk00516.txt blk00517.txt blk00518.txt blk00519.txt blk00520.txt blk00521.txt blk00522.txt blk00523.txt blk00524.txt blk00525.txt blk00526.txt blk00527.txt blk00528.txt blk00529.txt blk00530.txt blk00531.txt blk00532.txt blk00533.txt blk00534.txt blk00535.txt blk00536.txt blk00537.txt blk00538.txt blk00539.txt blk00540.txt blk00541.txt blk00542.txt blk00543.txt blk00544.txt blk00545.txt blk00546.txt blk00547.txt blk00548.txt blk00549.txt blk00550.txt blk00551.txt blk00552.txt blk00553.txt blk00554.txt blk00555.txt blk00556.txt blk00557.txt blk00558.txt blk00559.txt blk00560.txt blk00561.txt blk00562.txt blk00563.txt blk00564.txt blk00565.txt blk00566.txt blk00567.txt blk00568.txt blk00569.txt blk00570.txt blk00571.txt blk00572.txt blk00573.txt blk00574.txt blk00575.txt blk00576.txt blk00577.txt blk00578.txt blk00579.txt blk00580.txt blk00581.txt blk00582.txt blk00583.txt blk00584.txt blk00585.txt blk00586.txt blk00587.txt blk00588.txt blk00589.txt blk00590.txt blk00591.txt blk00592.txt blk00593.txt blk00594.txt blk00595.txt blk00596.txt blk00597.txt blk00598.txt blk00599.txt blk00600.txt blk00601.txt blk00602.txt blk00603.txt blk00604.txt blk00605.txt blk00606.txt blk00607.txt blk00608.txt blk00609.txt blk00610.txt blk00611.txt blk00612.txt blk00613.txt blk00614.txt blk00615.txt blk00616.txt blk00617.txt blk00618.txt blk00619.txt blk00620.txt blk00621.txt blk00622.txt blk00623.txt blk00624.txt blk00625.txt blk00626.txt blk00627.txt blk00628.txt blk00629.txt blk00630.txt blk00631.txt blk00632.txt blk00633.txt blk00634.txt blk00635.txt blk00636.txt blk00637.txt blk00638.txt blk00639.txt blk00640.txt blk00641.txt blk00642.txt blk00643.txt blk00644.txt blk00645.txt blk00646.txt blk00647.txt blk00648.txt blk00649.txt blk00650.txt blk00651.txt blk00652.txt blk00653.txt blk00654.txt blk00655.txt blk00656.txt blk00657.txt blk00658.txt blk00659.txt blk00660.txt blk00661.txt blk00662.txt blk00663.txt blk00664.txt blk00665.txt blk00666.txt blk00667.txt blk00668.txt blk00669.txt blk00670.txt blk00671.txt blk00672.txt blk00673.txt blk00674.txt blk00675.txt blk00676.txt blk00677.txt blk00678.txt blk00679.txt blk00680.txt blk00681.txt blk00682.txt blk00683.txt blk00684.txt blk00685.txt blk00686.txt blk00687.txt blk00688.txt blk00689.txt blk00690.txt blk00691.txt blk00692.txt blk00693.txt blk00694.txt blk00695.txt blk00696.txt blk00697.txt blk00698.txt blk00699.txt blk00700.txt blk00701.txt blk00702.txt blk00703.txt blk00704.txt blk00705.txt blk00706.txt blk00707.txt blk00708.txt blk00709.txt blk00710.txt blk00711.txt blk00712.txt blk00713.txt blk00714.txt blk00715.txt blk00716.txt blk00717.txt blk00718.txt blk00719.txt blk00720.txt blk00721.txt blk00722.txt blk00723.txt blk00724.txt blk00725.txt blk00726.txt blk00727.txt blk00728.txt blk00729.txt blk00730.txt blk00731.txt blk00732.txt blk00733.txt blk00734.txt blk00735.txt blk00736.txt blk00737.txt blk00738.txt blk00739.txt blk00740.txt blk00741.txt blk00742.txt blk00743.txt blk00744.txt blk00745.txt blk00746.txt blk00747.txt blk00748.txt blk00749.txt blk00750.txt blk00751.txt blk00752.txt blk00753.txt blk00754.txt blk00755.txt blk00756.txt blk00757.txt blk00758.txt blk00759.txt blk00760.txt blk00761.txt blk00762.txt blk00763.txt blk00764.txt blk00765.txt blk00766.txt blk00767.txt blk00768.txt blk00769.txt blk00770.txt blk00771.txt blk00772.txt blk00773.txt blk00774.txt blk00775.txt blk00776.txt blk00777.txt blk00778.txt blk00779.txt blk00780.txt blk00781.txt blk00782.txt blk00783.txt blk00784.txt blk00785.txt blk00786.txt blk00787.txt blk00788.txt blk00789.txt blk00790.txt blk00791.txt blk00792.txt blk00793.txt blk00794.txt blk00795.txt blk00796.txt blk00797.txt blk00798.txt blk00799.txt blk00800.txt blk00801.txt blk00802.txt blk00803.txt blk00804.txt blk00805.txt blk00806.txt blk00807.txt blk00808.txt blk00809.txt blk00810.txt blk00811.txt blk00812.txt blk00813.txt blk00814.txt blk00815.txt blk00816.txt blk00817.txt blk00818.txt blk00819.txt blk00820.txt blk00821.txt blk00822.txt blk00823.txt blk00824.txt blk00825.txt blk00826.txt blk00827.txt blk00828.txt blk00829.txt blk00830.txt blk00831.txt blk00832.txt blk00833.txt blk00834.txt blk00835.txt blk00836.txt blk00837.txt blk00838.txt blk00839.txt blk00840.txt blk00841.txt blk00842.txt blk00843.txt blk00844.txt blk00845.txt blk00846.txt blk00847.txt blk00848.txt blk00849.txt blk00850.txt blk00851.txt blk00852.txt blk00853.txt blk00854.txt blk00855.txt blk00856.txt blk00857.txt blk00858.txt blk00859.txt blk00860.txt blk00861.txt blk00862.txt blk00863.txt blk00864.txt blk00865.txt blk00866.txt blk00867.txt blk00868.txt blk00869.txt blk00870.txt blk00871.txt blk00872.txt blk00873.txt blk00874.txt blk00875.txt blk00876.txt blk00877.txt blk00878.txt blk00879.txt blk00880.txt blk00881.txt blk00882.txt blk00883.txt blk00884.txt blk00885.txt blk00886.txt blk00887.txt blk00888.txt blk00889.txt blk00890.txt blk00891.txt blk00892.txt blk00893.txt blk00894.txt blk00895.txt blk00896.txt blk00897.txt blk00898.txt blk00899.txt blk00900.txt blk00901.txt blk00902.txt blk00903.txt blk00904.txt blk00905.txt blk00906.txt blk00907.txt blk00908.txt blk00909.txt blk00910.txt blk00911.txt blk00912.txt blk00913.txt blk00914.txt blk00915.txt blk00916.txt blk00917.txt blk00918.txt blk00919.txt blk00920.txt blk00921.txt blk00922.txt blk00923.txt blk00924.txt blk00925.txt blk00926.txt blk00927.txt blk00928.txt blk00929.txt blk00930.txt blk00931.txt blk00932.txt blk00933.txt blk00934.txt blk00935.txt blk00936.txt blk00937.txt blk00938.txt blk00939.txt blk00940.txt blk00941.txt blk00942.txt blk00943.txt blk00944.txt blk00945.txt blk00946.txt blk00947.txt blk00948.txt blk00949.txt blk00950.txt blk00951.txt blk00952.txt blk00953.txt blk00954.txt blk00955.txt blk00956.txt blk00957.txt blk00958.txt blk00959.txt blk00960.txt blk00961.txt blk00962.txt blk00963.txt blk00964.txt blk00965.txt blk00966.txt blk00967.txt blk00968.txt blk00969.txt blk00970.txt blk00971.txt blk00972.txt blk00973.txt blk00974.txt blk00975.txt blk00976.txt blk00977.txt blk00978.txt blk00979.txt blk00980.txt blk00981.txt blk00982.txt blk00983.txt blk00984.txt blk00985.txt blk00986.txt blk00987.txt blk00988.txt blk00989.txt blk00990.txt blk00991.txt blk00992.txt blk00993.txt blk00994.txt blk00995.txt blk00996.txt blk00997.txt blk00998.txt blk00999.txt blk01000.txt blk01001.txt blk01002.txt blk01003.txt blk01004.txt blk01005.txt blk01006.txt blk01007.txt blk01008.txt blk01009.txt blk01010.txt blk01011.txt blk01012.txt blk01013.txt blk01014.txt blk01015.txt blk01016.txt blk01017.txt blk01018.txt blk01019.txt blk01020.txt blk01021.txt blk01022.txt blk01023.txt blk01024.txt blk01025.txt blk01026.txt blk01027.txt blk01028.txt blk01029.txt blk01030.txt blk01031.txt blk01032.txt blk01033.txt blk01034.txt blk01035.txt blk01036.txt blk01037.txt blk01038.txt blk01039.txt blk01040.txt blk01041.txt blk01042.txt blk01043.txt blk01044.txt blk01045.txt blk01046.txt blk01047.txt blk01048.txt blk01049.txt blk01050.txt blk01051.txt blk01052.txt blk01053.txt blk01054.txt blk01055.txt blk01056.txt blk01057.txt blk01058.txt blk01059.txt blk01060.txt blk01061.txt blk01062.txt blk01063.txt blk01064.txt blk01065.txt blk01066.txt blk01067.txt blk01068.txt blk01069.txt blk01070.txt blk01071.txt blk01072.txt blk01073.txt blk01074.txt blk01075.txt blk01076.txt blk01077.txt blk01078.txt blk01079.txt blk01080.txt blk01081.txt blk01082.txt blk01083.txt blk01084.txt blk01085.txt blk01086.txt blk01087.txt blk01088.txt blk01089.txt blk01090.txt blk01091.txt blk01092.txt blk01093.txt blk01094.txt blk01095.txt blk01096.txt blk01097.txt blk01098.txt blk01099.txt blk01100.txt blk01101.txt blk01102.txt blk01103.txt blk01104.txt blk01105.txt blk01106.txt blk01107.txt blk01108.txt blk01109.txt blk01110.txt blk01111.txt blk01112.txt blk01113.txt blk01114.txt blk01115.txt blk01116.txt blk01117.txt blk01118.txt blk01119.txt blk01120.txt blk01121.txt blk01122.txt blk01123.txt blk01124.txt blk01125.txt blk01126.txt blk01127.txt blk01128.txt blk01129.txt blk01130.txt blk01131.txt blk01132.txt blk01133.txt blk01134.txt blk01135.txt blk01136.txt blk01137.txt blk01138.txt blk01139.txt blk01140.txt blk01141.txt blk01142.txt blk01143.txt blk01144.txt blk01145.txt blk01146.txt blk01147.txt blk01148.txt blk01149.txt blk01150.txt blk01151.txt blk01152.txt blk01153.txt blk01154.txt blk01155.txt blk01156.txt blk01157.txt blk01158.txt blk01159.txt blk01160.txt blk01161.txt blk01162.txt blk01163.txt blk01164.txt blk01165.txt blk01166.txt blk01167.txt blk01168.txt blk01169.txt blk01170.txt blk01171.txt blk01172.txt blk01173.txt blk01174.txt blk01175.txt blk01176.txt blk01177.txt blk01178.txt blk01179.txt blk01180.txt blk01181.txt blk01182.txt blk01183.txt blk01184.txt blk01185.txt blk01186.txt blk01187.txt blk01188.txt blk01189.txt blk01190.txt blk01191.txt blk01192.txt blk01193.txt blk01194.txt blk01195.txt blk01196.txt blk01197.txt blk01198.txt blk01199.txt blk01200.txt blk01201.txt blk01202.txt blk01203.txt blk01204.txt blk01205.txt blk01206.txt blk01207.txt blk01208.txt blk01209.txt blk01210.txt blk01211.txt blk01212.txt blk01213.txt blk01214.txt blk01215.txt blk01216.txt blk01217.txt blk01218.txt blk01219.txt blk01220.txt blk01221.txt blk01222.txt blk01223.txt blk01224.txt blk01225.txt blk01226.txt blk01227.txt blk01228.txt blk01229.txt blk01230.txt blk01231.txt blk01232.txt blk01233.txt blk01234.txt blk01235.txt blk01236.txt blk01237.txt blk01238.txt blk01239.txt blk01240.txt blk01241.txt blk01242.txt blk01243.txt blk01244.txt blk01245.txt blk01246.txt blk01247.txt blk01248.txt blk01249.txt blk01250.txt blk01251.txt blk01252.txt blk01253.txt blk01254.txt blk01255.txt blk01256.txt blk01257.txt blk01258.txt blk01259.txt blk01260.txt blk01261.txt blk01262.txt blk01263.txt blk01264.txt blk01265.txt blk01266.txt blk01267.txt blk01268.txt blk01269.txt blk01270.txt blk01271.txt blk01272.txt blk01273.txt blk01274.txt blk01275.txt blk01276.txt blk01277.txt blk01278.txt blk01279.txt blk01280.txt blk01281.txt blk01282.txt blk01283.txt blk01284.txt blk01285.txt blk01286.txt blk01287.txt blk01288.txt blk01289.txt blk01290.txt blk01291.txt blk01292.txt blk01293.txt blk01294.txt blk01295.txt blk01296.txt blk01297.txt blk01298.txt blk01299.txt blk01300.txt blk01301.txt blk01302.txt blk01303.txt blk01304.txt blk01305.txt blk01306.txt blk01307.txt blk01308.txt blk01309.txt blk01310.txt blk01311.txt blk01312.txt blk01313.txt blk01314.txt blk01315.txt blk01316.txt blk01317.txt blk01318.txt blk01319.txt blk01320.txt blk01321.txt blk01322.txt blk01323.txt blk01324.txt blk01325.txt blk01326.txt blk01327.txt blk01328.txt blk01329.txt blk01330.txt blk01331.txt blk01332.txt blk01333.txt blk01334.txt blk01335.txt blk01336.txt blk01337.txt blk01338.txt blk01339.txt blk01340.txt blk01341.txt blk01342.txt blk01343.txt blk01344.txt blk01345.txt blk01346.txt blk01347.txt blk01348.txt blk01349.txt blk01350.txt blk01351.txt blk01352.txt blk01353.txt blk01354.txt blk01355.txt blk01356.txt blk01357.txt blk01358.txt blk01359.txt blk01360.txt blk01361.txt blk01362.txt blk01363.txt blk01364.txt blk01365.txt blk01366.txt blk01367.txt blk01368.txt blk01369.txt blk01370.txt blk01371.txt blk01372.txt blk01373.txt blk01374.txt blk01375.txt blk01376.txt blk01377.txt blk01378.txt blk01379.txt blk01380.txt blk01381.txt blk01382.txt blk01383.txt blk01384.txt blk01385.txt blk01386.txt blk01387.txt blk01388.txt blk01389.txt blk01390.txt blk01391.txt blk01392.txt blk01393.txt blk01394.txt blk01395.txt blk01396.txt blk01397.txt blk01398.txt blk01399.txt blk01400.txt blk01401.txt blk01402.txt blk01403.txt blk01404.txt blk01405.txt blk01406.txt blk01407.txt blk01408.txt blk01409.txt blk01410.txt blk01411.txt blk01412.txt blk01413.txt blk01414.txt blk01415.txt blk01416.txt blk01417.txt blk01418.txt blk01419.txt blk01420.txt blk01421.txt blk01422.txt blk01423.txt blk01424.txt blk01425.txt blk01426.txt blk01427.txt blk01428.txt blk01429.txt blk01430.txt blk01431.txt blk01432.txt blk01433.txt blk01434.txt blk01435.txt blk01436.txt blk01437.txt blk01438.txt blk01439.txt blk01440.txt blk01441.txt blk01442.txt blk01443.txt blk01444.txt blk01445.txt blk01446.txt blk01447.txt blk01448.txt blk01449.txt blk01450.txt blk01451.txt blk01452.txt blk01453.txt blk01454.txt blk01455.txt blk01456.txt blk01457.txt blk01458.txt blk01459.txt blk01460.txt blk01461.txt blk01462.txt blk01463.txt blk01464.txt blk01465.txt blk01466.txt blk01467.txt blk01468.txt blk01469.txt blk01470.txt blk01471.txt blk01472.txt blk01473.txt blk01474.txt blk01475.txt blk01476.txt blk01477.txt blk01478.txt blk01479.txt blk01480.txt blk01481.txt blk01482.txt blk01483.txt blk01484.txt blk01485.txt blk01486.txt blk01487.txt blk01488.txt blk01489.txt blk01490.txt blk01491.txt blk01492.txt blk01493.txt blk01494.txt blk01495.txt blk01496.txt blk01497.txt blk01498.txt blk01499.txt blk01500.txt blk01501.txt blk01502.txt blk01503.txt blk01504.txt blk01505.txt blk01506.txt blk01507.txt blk01508.txt blk01509.txt blk01510.txt blk01511.txt blk01512.txt blk01513.txt blk01514.txt blk01515.txt blk01516.txt blk01517.txt blk01518.txt blk01519.txt blk01520.txt blk01521.txt blk01522.txt blk01523.txt blk01524.txt blk01525.txt blk01526.txt blk01527.txt blk01528.txt blk01529.txt blk01530.txt blk01531.txt blk01532.txt blk01533.txt blk01534.txt blk01535.txt blk01536.txt blk01537.txt blk01538.txt blk01539.txt blk01540.txt blk01541.txt blk01542.txt blk01543.txt blk01544.txt blk01545.txt blk01546.txt blk01547.txt blk01548.txt blk01549.txt blk01550.txt blk01551.txt blk01552.txt blk01553.txt blk01554.txt blk01555.txt blk01556.txt blk01557.txt blk01558.txt blk01559.txt blk01560.txt blk01561.txt blk01562.txt blk01563.txt blk01564.txt blk01565.txt blk01566.txt blk01567.txt blk01568.txt blk01569.txt blk01570.txt blk01571.txt blk01572.txt blk01573.txt blk01574.txt blk01575.txt blk01576.txt blk01577.txt blk01578.txt blk01579.txt blk01580.txt blk01581.txt blk01582.txt blk01583.txt blk01584.txt blk01585.txt blk01586.txt blk01587.txt blk01588.txt blk01589.txt blk01590.txt blk01591.txt blk01592.txt blk01593.txt blk01594.txt blk01595.txt blk01596.txt blk01597.txt blk01598.txt blk01599.txt blk01600.txt blk01601.txt blk01602.txt blk01603.txt blk01604.txt blk01605.txt blk01606.txt blk01607.txt blk01608.txt blk01609.txt blk01610.txt blk01611.txt blk01612.txt blk01613.txt blk01614.txt blk01615.txt blk01616.txt blk01617.txt blk01618.txt blk01619.txt blk01620.txt blk01621.txt blk01622.txt blk01623.txt blk01624.txt blk01625.txt blk01626.txt blk01627.txt blk01628.txt blk01629.txt blk01630.txt blk01631.txt blk01632.txt blk01633.txt blk01634.txt blk01635.txt blk01636.txt blk01637.txt blk01638.txt blk01639.txt blk01640.txt blk01641.txt blk01642.txt blk01643.txt blk01644.txt blk01645.txt blk01646.txt blk01647.txt blk01648.txt blk01649.txt blk01650.txt blk01651.txt blk01652.txt blk01653.txt blk01654.txt blk01655.txt blk01656.txt blk01657.txt blk01658.txt blk01659.txt blk01660.txt blk01661.txt blk01662.txt blk01663.txt blk01664.txt blk01665.txt blk01666.txt blk01667.txt blk01668.txt blk01669.txt blk01670.txt blk01671.txt blk01672.txt blk01673.txt blk01674.txt blk01675.txt blk01676.txt blk01677.txt blk01678.txt blk01679.txt blk01680.txt blk01681.txt blk01682.txt blk01683.txt blk01684.txt blk01685.txt blk01686.txt blk01687.txt blk01688.txt blk01689.txt blk01690.txt blk01691.txt blk01692.txt blk01693.txt blk01694.txt blk01695.txt blk01696.txt blk01697.txt blk01698.txt blk01699.txt blk01700.txt blk01701.txt blk01702.txt blk01703.txt blk01704.txt blk01705.txt blk01706.txt blk01707.txt blk01708.txt blk01709.txt blk01710.txt blk01711.txt blk01712.txt blk01713.txt blk01714.txt blk01715.txt blk01716.txt blk01717.txt blk01718.txt blk01719.txt blk01720.txt blk01721.txt blk01722.txt blk01723.txt blk01724.txt blk01725.txt blk01726.txt blk01727.txt blk01728.txt blk01729.txt blk01730.txt blk01731.txt blk01732.txt blk01733.txt blk01734.txt blk01735.txt blk01736.txt blk01737.txt blk01738.txt blk01739.txt blk01740.txt blk01741.txt blk01742.txt blk01743.txt blk01744.txt blk01745.txt blk01746.txt blk01747.txt blk01748.txt blk01749.txt blk01750.txt blk01751.txt blk01752.txt blk01753.txt blk01754.txt blk01755.txt blk01756.txt blk01757.txt blk01758.txt blk01759.txt blk01760.txt blk01761.txt blk01762.txt blk01763.txt blk01764.txt blk01765.txt blk01766.txt blk01767.txt blk01768.txt blk01769.txt blk01770.txt blk01771.txt blk01772.txt blk01773.txt blk01774.txt blk01775.txt blk01776.txt blk01777.txt blk01778.txt blk01779.txt blk01780.txt blk01781.txt blk01782.txt blk01783.txt blk01784.txt blk01785.txt blk01786.txt blk01787.txt blk01788.txt blk01789.txt blk01790.txt blk01791.txt blk01792.txt blk01793.txt blk01794.txt blk01795.txt blk01796.txt blk01797.txt blk01798.txt blk01799.txt blk01800.txt blk01801.txt blk01802.txt blk01803.txt blk01804.txt blk01805.txt blk01806.txt blk01807.txt blk01808.txt blk01809.txt blk01810.txt blk01811.txt blk01812.txt blk01813.txt blk01814.txt blk01815.txt blk01816.txt blk01817.txt blk01818.txt blk01819.txt blk01820.txt blk01821.txt blk01822.txt blk01823.txt blk01824.txt blk01825.txt blk01826.txt blk01827.txt blk01828.txt blk01829.txt blk01830.txt blk01831.txt blk01832.txt blk01833.txt blk01834.txt blk01835.txt blk01836.txt blk01837.txt blk01838.txt blk01839.txt blk01840.txt blk01841.txt blk01842.txt blk01843.txt blk01844.txt blk01845.txt blk01846.txt blk01847.txt blk01848.txt blk01849.txt blk01850.txt blk01851.txt blk01852.txt blk01853.txt blk01854.txt blk01855.txt blk01856.txt blk01857.txt blk01858.txt blk01859.txt blk01860.txt blk01861.txt blk01862.txt blk01863.txt blk01864.txt blk01865.txt blk01866.txt blk01867.txt blk01868.txt blk01869.txt blk01870.txt blk01871.txt blk01872.txt blk01873.txt blk01874.txt blk01875.txt blk01876.txt blk01877.txt blk01878.txt blk01879.txt blk01880.txt blk01881.txt blk01882.txt blk01883.txt blk01884.txt blk01885.txt blk01886.txt blk01887.txt blk01888.txt blk01889.txt blk01890.txt blk01891.txt blk01892.txt blk01893.txt blk01894.txt blk01895.txt blk01896.txt blk01897.txt blk01898.txt blk01899.txt blk01900.txt blk01901.txt blk01902.txt blk01903.txt blk01904.txt blk01905.txt blk01906.txt blk01907.txt blk01908.txt blk01909.txt blk01910.txt blk01911.txt blk01912.txt blk01913.txt blk01914.txt blk01915.txt blk01916.txt blk01917.txt blk01918.txt blk01919.txt blk01920.txt blk01921.txt blk01922.txt blk01923.txt blk01924.txt blk01925.txt blk01926.txt blk01927.txt blk01928.txt blk01929.txt blk01930.txt blk01931.txt blk01932.txt blk01933.txt blk01934.txt blk01935.txt blk01936.txt blk01937.txt blk01938.txt blk01939.txt blk01940.txt blk01941.txt blk01942.txt blk01943.txt blk01944.txt blk01945.txt blk01946.txt blk01947.txt blk01948.txt blk01949.txt blk01950.txt blk01951.txt blk01952.txt blk01953.txt blk01954.txt blk01955.txt blk01956.txt blk01957.txt blk01958.txt blk01959.txt blk01960.txt blk01961.txt blk01962.txt blk01963.txt blk01964.txt blk01965.txt blk01966.txt blk01967.txt blk01968.txt blk01969.txt blk01970.txt blk01971.txt blk01972.txt blk01973.txt blk01974.txt blk01975.txt blk01976.txt blk01977.txt blk01978.txt blk01979.txt blk01980.txt blk01981.txt blk01982.txt blk01983.txt blk01984.txt blk01985.txt blk01986.txt blk01987.txt blk01988.txt blk01989.txt blk01990.txt blk01991.txt blk01992.txt blk01993.txt blk01994.txt blk01995.txt blk01996.txt blk01997.txt blk01998.txt blk01999.txt blk02000.txt blk02001.txt blk02002.txt blk02003.txt blk02004.txt blk02005.txt blk02006.txt blk02007.txt blk02008.txt blk02009.txt blk02010.txt blk02011.txt blk02012.txt blk02013.txt blk02014.txt blk02015.txt blk02016.txt blk02017.txt blk02018.txt blk02019.txt blk02020.txt blk02021.txt blk02022.txt blk02023.txt blk02024.txt blk02025.txt blk02026.txt blk02027.txt blk02028.txt blk02029.txt blk02030.txt blk02031.txt blk02032.txt blk02033.txt blk02034.txt blk02035.txt blk02036.txt blk02037.txt blk02038.txt blk02039.txt blk02040.txt blk02041.txt blk02042.txt blk02043.txt blk02044.txt blk02045.txt blk02046.txt blk02047.txt blk02048.txt blk02049.txt blk02050.txt blk02051.txt blk02052.txt blk02053.txt blk02054.txt blk02055.txt blk02056.txt blk02057.txt blk02058.txt blk02059.txt blk02060.txt blk02061.txt blk02062.txt blk02063.txt blk02064.txt blk02065.txt blk02066.txt blk02067.txt blk02068.txt blk02069.txt blk02070.txt blk02071.txt blk02072.txt blk02073.txt blk02074.txt blk02075.txt blk02076.txt blk02077.txt blk02078.txt blk02079.txt blk02080.txt blk02081.txt blk02082.txt blk02083.txt blk02084.txt blk02085.txt blk02086.txt blk02087.txt blk02088.txt blk02089.txt blk02090.txt blk02091.txt blk02092.txt blk02093.txt blk02094.txt blk02095.txt blk02096.txt blk02097.txt blk02098.txt blk02099.txt blk02100.txt blk02101.txt blk02102.txt blk02103.txt blk02104.txt blk02105.txt blk02106.txt blk02107.txt blk02108.txt blk02109.txt blk02110.txt blk02111.txt blk02112.txt blk02113.txt blk02114.txt blk02115.txt blk02116.txt blk02117.txt blk02118.txt blk02119.txt blk02120.txt blk02121.txt blk02122.txt blk02123.txt blk02124.txt blk02125.txt blk02126.txt blk02127.txt blk02128.txt blk02129.txt blk02130.txt blk02131.txt blk02132.txt blk02133.txt blk02134.txt blk02135.txt blk02136.txt blk02137.txt blk02138.txt blk02139.txt blk02140.txt blk02141.txt blk02142.txt blk02143.txt blk02144.txt blk02145.txt blk02146.txt blk02147.txt blk02148.txt blk02149.txt blk02150.txt blk02151.txt blk02152.txt blk02153.txt blk02154.txt blk02155.txt blk02156.txt blk02157.txt blk02158.txt blk02159.txt blk02160.txt blk02161.txt blk02162.txt blk02163.txt blk02164.txt blk02165.txt blk02166.txt blk02167.txt blk02168.txt blk02169.txt blk02170.txt blk02171.txt blk02172.txt blk02173.txt blk02174.txt blk02175.txt blk02176.txt blk02177.txt blk02178.txt blk02179.txt blk02180.txt blk02181.txt blk02182.txt blk02183.txt blk02184.txt blk02185.txt blk02186.txt blk02187.txt blk02188.txt blk02189.txt blk02190.txt blk02191.txt blk02192.txt blk02193.txt blk02194.txt blk02195.txt blk02196.txt blk02197.txt blk02198.txt blk02199.txt blk02200.txt blk02201.txt blk02202.txt blk02203.txt blk02204.txt blk02205.txt blk02206.txt blk02207.txt blk02208.txt blk02209.txt blk02210.txt blk02211.txt blk02212.txt blk02213.txt blk02214.txt blk02215.txt blk02216.txt blk02217.txt blk02218.txt blk02219.txt blk02220.txt blk02221.txt blk02222.txt blk02223.txt blk02224.txt blk02225.txt blk02226.txt blk02227.txt blk02228.txt blk02229.txt blk02230.txt blk02231.txt blk02232.txt blk02233.txt blk02234.txt blk02235.txt blk02236.txt blk02237.txt blk02238.txt blk02239.txt blk02240.txt blk02241.txt blk02242.txt blk02243.txt blk02244.txt blk02245.txt blk02246.txt blk02247.txt blk02248.txt blk02249.txt blk02250.txt blk02251.txt blk02252.txt blk02253.txt blk02254.txt blk02255.txt blk02256.txt blk02257.txt blk02258.txt blk02259.txt blk02260.txt blk02261.txt blk02262.txt blk02263.txt blk02264.txt blk02265.txt blk02266.txt blk02267.txt blk02268.txt blk02269.txt blk02270.txt blk02271.txt blk02272.txt blk02273.txt blk02274.txt blk02275.txt blk02276.txt blk02277.txt blk02278.txt blk02279.txt blk02280.txt blk02281.txt blk02282.txt blk02283.txt blk02284.txt blk02285.txt blk02286.txt blk02287.txt blk02288.txt blk02289.txt blk02290.txt blk02291.txt blk02292.txt blk02293.txt blk02294.txt blk02295.txt blk02296.txt blk02297.txt blk02298.txt blk02299.txt blk02300.txt blk02301.txt blk02302.txt blk02303.txt blk02304.txt blk02305.txt blk02306.txt blk02307.txt blk02308.txt blk02309.txt blk02310.txt blk02311.txt blk02312.txt blk02313.txt blk02314.txt blk02315.txt blk02316.txt blk02317.txt blk02318.txt blk02319.txt blk02320.txt blk02321.txt blk02322.txt blk02323.txt blk02324.txt blk02325.txt blk02326.txt blk02327.txt blk02328.txt blk02329.txt blk02330.txt blk02331.txt blk02332.txt blk02333.txt blk02334.txt blk02335.txt blk02336.txt blk02337.txt blk02338.txt blk02339.txt blk02340.txt blk02341.txt blk02342.txt blk02343.txt blk02344.txt blk02345.txt blk02346.txt blk02347.txt blk02348.txt blk02349.txt blk02350.txt blk02351.txt blk02352.txt blk02353.txt blk02354.txt blk02355.txt blk02356.txt blk02357.txt blk02358.txt blk02359.txt blk02360.txt blk02361.txt blk02362.txt blk02363.txt blk02364.txt blk02365.txt blk02366.txt blk02367.txt blk02368.txt blk02369.txt blk02370.txt blk02371.txt blk02372.txt blk02373.txt blk02374.txt blk02375.txt blk02376.txt blk02377.txt blk02378.txt blk02379.txt blk02380.txt blk02381.txt blk02382.txt blk02383.txt blk02384.txt blk02385.txt blk02386.txt blk02387.txt blk02388.txt blk02389.txt blk02390.txt blk02391.txt blk02392.txt blk02393.txt blk02394.txt blk02395.txt blk02396.txt blk02397.txt blk02398.txt blk02399.txt blk02400.txt blk02401.txt blk02402.txt blk02403.txt blk02404.txt blk02405.txt blk02406.txt blk02407.txt blk02408.txt blk02409.txt blk02410.txt blk02411.txt blk02412.txt blk02413.txt blk02414.txt blk02415.txt blk02416.txt blk02417.txt blk02418.txt blk02419.txt blk02420.txt blk02421.txt blk02422.txt blk02423.txt blk02424.txt blk02425.txt blk02426.txt blk02427.txt blk02428.txt blk02429.txt blk02430.txt blk02431.txt blk02432.txt blk02433.txt blk02434.txt blk02435.txt blk02436.txt blk02437.txt blk02438.txt blk02439.txt blk02440.txt blk02441.txt blk02442.txt blk02443.txt blk02444.txt blk02445.txt blk02446.txt blk02447.txt blk02448.txt blk02449.txt blk02450.txt blk02451.txt blk02452.txt blk02453.txt blk02454.txt blk02455.txt blk02456.txt blk02457.txt blk02458.txt blk02459.txt blk02460.txt blk02461.txt blk02462.txt blk02463.txt blk02464.txt blk02465.txt blk02466.txt blk02467.txt blk02468.txt blk02469.txt blk02470.txt blk02471.txt blk02472.txt blk02473.txt blk02474.txt blk02475.txt blk02476.txt blk02477.txt blk02478.txt blk02479.txt blk02480.txt blk02481.txt blk02482.txt blk02483.txt blk02484.txt blk02485.txt blk02486.txt blk02487.txt blk02488.txt blk02489.txt blk02490.txt blk02491.txt blk02492.txt blk02493.txt blk02494.txt blk02495.txt blk02496.txt blk02497.txt blk02498.txt blk02499.txt blk02500.txt blk02501.txt blk02502.txt blk02503.txt blk02504.txt blk02505.txt blk02506.txt blk02507.txt blk02508.txt blk02509.txt blk02510.txt blk02511.txt blk02512.txt blk02513.txt blk02514.txt blk02515.txt blk02516.txt blk02517.txt blk02518.txt blk02519.txt blk02520.txt blk02521.txt blk02522.txt blk02523.txt blk02524.txt blk02525.txt blk02526.txt blk02527.txt blk02528.txt blk02529.txt blk02530.txt blk02531.txt blk02532.txt blk02533.txt blk02534.txt blk02535.txt blk02536.txt blk02537.txt blk02538.txt blk02539.txt blk02540.txt blk02541.txt blk02542.txt blk02543.txt blk02544.txt blk02545.txt blk02546.txt blk02547.txt blk02548.txt blk02549.txt blk02550.txt blk02551.txt blk02552.txt blk02553.txt blk02554.txt blk02555.txt blk02556.txt blk02557.txt blk02558.txt blk02559.txt blk02560.txt blk02561.txt blk02562.txt blk02563.txt blk02564.txt blk02565.txt blk02566.txt blk02567.txt blk02568.txt blk02569.txt blk02570.txt blk02571.txt blk02572.txt blk02573.txt blk02574.txt blk02575.txt blk02576.txt blk02577.txt blk02578.txt blk02579.txt blk02580.txt blk02581.txt blk02582.txt blk02583.txt blk02584.txt blk02585.txt blk02586.txt blk02587.txt blk02588.txt blk02589.txt blk02590.txt blk02591.txt blk02592.txt blk02593.txt blk02594.txt blk02595.txt blk02596.txt blk02597.txt blk02598.txt blk02599.txt blk02600.txt blk02601.txt blk02602.txt blk02603.txt blk02604.txt blk02605.txt blk02606.txt blk02607.txt blk02608.txt blk02609.txt blk02610.txt blk02611.txt blk02612.txt blk02613.txt blk02614.txt blk02615.txt blk02616.txt blk02617.txt blk02618.txt blk02619.txt blk02620.txt blk02621.txt blk02622.txt blk02623.txt blk02624.txt blk02625.txt blk02626.txt blk02627.txt blk02628.txt blk02629.txt blk02630.txt blk02631.txt blk02632.txt blk02633.txt blk02634.txt blk02635.txt blk02636.txt blk02637.txt blk02638.txt blk02639.txt blk02640.txt blk02641.txt blk02642.txt blk02643.txt blk02644.txt blk02645.txt blk02646.txt blk02647.txt blk02648.txt blk02649.txt blk02650.txt blk02651.txt blk02652.txt blk02653.txt blk02654.txt blk02655.txt blk02656.txt blk02657.txt blk02658.txt blk02659.txt blk02660.txt blk02661.txt blk02662.txt blk02663.txt blk02664.txt blk02665.txt blk02666.txt blk02667.txt blk02668.txt blk02669.txt blk02670.txt blk02671.txt blk02672.txt blk02673.txt blk02674.txt blk02675.txt blk02676.txt blk02677.txt blk02678.txt blk02679.txt blk02680.txt blk02681.txt blk02682.txt blk02683.txt blk02684.txt blk02685.txt blk02686.txt blk02687.txt blk02688.txt blk02689.txt blk02690.txt blk02691.txt blk02692.txt blk02693.txt blk02694.txt blk02695.txt blk02696.txt blk02697.txt blk02698.txt blk02699.txt blk02700.txt blk02701.txt blk02702.txt blk02703.txt blk02704.txt blk02705.txt blk02706.txt blk02707.txt blk02708.txt blk02709.txt blk02710.txt blk02711.txt blk02712.txt blk02713.txt blk02714.txt blk02715.txt blk02716.txt blk02717.txt blk02718.txt blk02719.txt blk02720.txt blk02721.txt blk02722.txt blk02723.txt blk02724.txt blk02725.txt blk02726.txt blk02727.txt blk02728.txt blk02729.txt blk02730.txt blk02731.txt blk02732.txt blk02733.txt blk02734.txt blk02735.txt blk02736.txt blk02737.txt blk02738.txt blk02739.txt blk02740.txt blk02741.txt blk02742.txt blk02743.txt blk02744.txt blk02745.txt blk02746.txt blk02747.txt blk02748.txt blk02749.txt blk02750.txt blk02751.txt blk02752.txt blk02753.txt blk02754.txt blk02755.txt blk02756.txt blk02757.txt blk02758.txt blk02759.txt blk02760.txt blk02761.txt blk02762.txt blk02763.txt blk02764.txt blk02765.txt blk02766.txt blk02767.txt blk02768.txt blk02769.txt blk02770.txt blk02771.txt blk02772.txt blk02773.txt blk02774.txt blk02775.txt blk02776.txt blk02777.txt blk02778.txt blk02779.txt blk02780.txt blk02781.txt blk02782.txt blk02783.txt blk02784.txt blk02785.txt blk02786.txt blk02787.txt blk02788.txt blk02789.txt blk02790.txt blk02791.txt blk02792.txt blk02793.txt blk02794.txt blk02795.txt blk02796.txt blk02797.txt blk02798.txt blk02799.txt blk02800.txt blk02801.txt blk02802.txt blk02803.txt blk02804.txt blk02805.txt blk02806.txt blk02807.txt blk02808.txt blk02809.txt blk02810.txt blk02811.txt blk02812.txt blk02813.txt blk02814.txt blk02815.txt blk02816.txt blk02817.txt blk02818.txt blk02819.txt blk02820.txt blk02821.txt blk02822.txt blk02823.txt blk02824.txt blk02825.txt blk02826.txt blk02827.txt blk02828.txt blk02829.txt blk02830.txt blk02831.txt blk02832.txt blk02833.txt blk02834.txt blk02835.txt blk02836.txt blk02837.txt blk02838.txt blk02839.txt blk02840.txt blk02841.txt blk02842.txt blk02843.txt blk02844.txt blk02845.txt blk02846.txt blk02847.txt blk02848.txt blk02849.txt blk02850.txt blk02851.txt blk02852.txt blk02853.txt blk02854.txt blk02855.txt blk02856.txt blk02857.txt blk02858.txt blk02859.txt blk02860.txt blk02861.txt blk02862.txt blk02863.txt blk02864.txt blk02865.txt blk02866.txt blk02867.txt blk02868.txt blk02869.txt blk02870.txt blk02871.txt blk02872.txt blk02873.txt blk02874.txt blk02875.txt blk02876.txt blk02877.txt blk02878.txt blk02879.txt blk02880.txt blk02881.txt blk02882.txt blk02883.txt blk02884.txt blk02885.txt blk02886.txt blk02887.txt blk02888.txt blk02889.txt blk02890.txt blk02891.txt blk02892.txt blk02893.txt blk02894.txt blk02895.txt blk02896.txt blk02897.txt blk02898.txt blk02899.txt blk02900.txt blk02901.txt blk02902.txt blk02903.txt blk02904.txt blk02905.txt blk02906.txt blk02907.txt blk02908.txt blk02909.txt blk02910.txt blk02911.txt blk02912.txt blk02913.txt blk02914.txt blk02915.txt blk02916.txt blk02917.txt blk02918.txt blk02919.txt blk02920.txt blk02921.txt blk02922.txt blk02923.txt blk02924.txt blk02925.txt blk02926.txt blk02927.txt blk02928.txt blk02929.txt blk02930.txt blk02931.txt blk02932.txt blk02933.txt blk02934.txt blk02935.txt blk02936.txt blk02937.txt blk02938.txt blk02939.txt blk02940.txt blk02941.txt blk02942.txt blk02943.txt blk02944.txt blk02945.txt blk02946.txt blk02947.txt blk02948.txt blk02949.txt blk02950.txt blk02951.txt blk02952.txt blk02953.txt blk02954.txt blk02955.txt blk02956.txt blk02957.txt blk02958.txt blk02959.txt blk02960.txt blk02961.txt blk02962.txt blk02963.txt blk02964.txt blk02965.txt blk02966.txt blk02967.txt blk02968.txt blk02969.txt blk02970.txt blk02971.txt blk02972.txt blk02973.txt blk02974.txt blk02975.txt blk02976.txt blk02977.txt blk02978.txt blk02979.txt blk02980.txt blk02981.txt blk02982.txt blk02983.txt blk02984.txt blk02985.txt blk02986.txt blk02987.txt blk02988.txt blk02989.txt blk02990.txt blk02991.txt blk02992.txt blk02993.txt blk02994.txt blk02995.txt blk02996.txt blk02997.txt blk02998.txt blk02999.txt blk03000.txt blk03001.txt blk03002.txt blk03003.txt blk03004.txt blk03005.txt blk03006.txt blk03007.txt blk03008.txt blk03009.txt blk03010.txt blk03011.txt blk03012.txt blk03013.txt blk03014.txt blk03015.txt blk03016.txt blk03017.txt blk03018.txt blk03019.txt blk03020.txt blk03021.txt blk03022.txt blk03023.txt blk03024.txt blk03025.txt blk03026.txt blk03027.txt blk03028.txt blk03029.txt blk03030.txt blk03031.txt blk03032.txt blk03033.txt blk03034.txt blk03035.txt blk03036.txt blk03037.txt blk03038.txt blk03039.txt blk03040.txt blk03041.txt blk03042.txt blk03043.txt blk03044.txt blk03045.txt blk03046.txt blk03047.txt blk03048.txt blk03049.txt blk03050.txt blk03051.txt blk03052.txt blk03053.txt blk03054.txt blk03055.txt blk03056.txt blk03057.txt blk03058.txt blk03059.txt blk03060.txt blk03061.txt blk03062.txt blk03063.txt blk03064.txt blk03065.txt blk03066.txt blk03067.txt blk03068.txt blk03069.txt blk03070.txt blk03071.txt blk03072.txt blk03073.txt blk03074.txt blk03075.txt blk03076.txt blk03077.txt blk03078.txt blk03079.txt blk03080.txt blk03081.txt blk03082.txt blk03083.txt blk03084.txt blk03085.txt blk03086.txt blk03087.txt blk03088.txt blk03089.txt blk03090.txt blk03091.txt blk03092.txt blk03093.txt blk03094.txt blk03095.txt blk03096.txt blk03097.txt blk03098.txt blk03099.txt blk03100.txt blk03101.txt blk03102.txt blk03103.txt blk03104.txt blk03105.txt blk03106.txt blk03107.txt blk03108.txt blk03109.txt blk03110.txt blk03111.txt blk03112.txt blk03113.txt blk03114.txt blk03115.txt blk03116.txt blk03117.txt blk03118.txt blk03119.txt blk03120.txt blk03121.txt blk03122.txt blk03123.txt blk03124.txt blk03125.txt blk03126.txt blk03127.txt blk03128.txt blk03129.txt blk03130.txt blk03131.txt blk03132.txt blk03133.txt blk03134.txt blk03135.txt blk03136.txt blk03137.txt blk03138.txt blk03139.txt blk03140.txt blk03141.txt blk03142.txt blk03143.txt blk03144.txt blk03145.txt blk03146.txt blk03147.txt blk03148.txt blk03149.txt blk03150.txt blk03151.txt blk03152.txt blk03153.txt blk03154.txt blk03155.txt blk03156.txt blk03157.txt blk03158.txt blk03159.txt blk03160.txt blk03161.txt blk03162.txt blk03163.txt blk03164.txt blk03165.txt blk03166.txt blk03167.txt blk03168.txt blk03169.txt blk03170.txt blk03171.txt blk03172.txt blk03173.txt blk03174.txt blk03175.txt blk03176.txt blk03177.txt blk03178.txt blk03179.txt blk03180.txt blk03181.txt blk03182.txt blk03183.txt blk03184.txt blk03185.txt blk03186.txt blk03187.txt blk03188.txt blk03189.txt blk03190.txt blk03191.txt blk03192.txt blk03193.txt blk03194.txt blk03195.txt blk03196.txt blk03197.txt blk03198.txt blk03199.txt blk03200.txt blk03201.txt blk03202.txt blk03203.txt blk03204.txt blk03205.txt blk03206.txt blk03207.txt blk03208.txt blk03209.txt blk03210.txt blk03211.txt blk03212.txt blk03213.txt blk03214.txt blk03215.txt blk03216.txt blk03217.txt blk03218.txt blk03219.txt blk03220.txt blk03221.txt blk03222.txt blk03223.txt blk03224.txt blk03225.txt blk03226.txt blk03227.txt blk03228.txt blk03229.txt blk03230.txt blk03231.txt blk03232.txt blk03233.txt blk03234.txt blk03235.txt blk03236.txt blk03237.txt blk03238.txt blk03239.txt blk03240.txt blk03241.txt blk03242.txt blk03243.txt blk03244.txt blk03245.txt blk03246.txt blk03247.txt blk03248.txt blk03249.txt blk03250.txt blk03251.txt blk03252.txt blk03253.txt blk03254.txt blk03255.txt blk03256.txt blk03257.txt blk03258.txt blk03259.txt blk03260.txt blk03261.txt blk03262.txt blk03263.txt blk03264.txt blk03265.txt blk03266.txt blk03267.txt blk03268.txt blk03269.txt blk03270.txt blk03271.txt blk03272.txt blk03273.txt blk03274.txt blk03275.txt blk03276.txt blk03277.txt blk03278.txt blk03279.txt blk03280.txt blk03281.txt blk03282.txt blk03283.txt blk03284.txt blk03285.txt blk03286.txt blk03287.txt blk03288.txt blk03289.txt blk03290.txt blk03291.txt blk03292.txt blk03293.txt blk03294.txt blk03295.txt blk03296.txt blk03297.txt blk03298.txt blk03299.txt blk03300.txt blk03301.txt blk03302.txt blk03303.txt blk03304.txt blk03305.txt blk03306.txt blk03307.txt blk03308.txt blk03309.txt blk03310.txt blk03311.txt blk03312.txt blk03313.txt blk03314.txt blk03315.txt blk03316.txt blk03317.txt blk03318.txt blk03319.txt blk03320.txt blk03321.txt blk03322.txt blk03323.txt blk03324.txt blk03325.txt blk03326.txt blk03327.txt blk03328.txt blk03329.txt blk03330.txt blk03331.txt blk03332.txt blk03333.txt blk03334.txt blk03335.txt blk03336.txt blk03337.txt blk03338.txt blk03339.txt blk03340.txt blk03341.txt blk03342.txt blk03343.txt blk03344.txt blk03345.txt blk03346.txt blk03347.txt blk03348.txt blk03349.txt blk03350.txt blk03351.txt blk03352.txt blk03353.txt blk03354.txt blk03355.txt blk03356.txt blk03357.txt blk03358.txt blk03359.txt blk03360.txt blk03361.txt blk03362.txt blk03363.txt blk03364.txt blk03365.txt blk03366.txt blk03367.txt blk03368.txt blk03369.txt blk03370.txt blk03371.txt blk03372.txt blk03373.txt blk03374.txt blk03375.txt blk03376.txt blk03377.txt blk03378.txt blk03379.txt blk03380.txt blk03381.txt blk03382.txt blk03383.txt blk03384.txt blk03385.txt blk03386.txt blk03387.txt blk03388.txt blk03389.txt blk03390.txt blk03391.txt blk03392.txt blk03393.txt blk03394.txt blk03395.txt blk03396.txt blk03397.txt blk03398.txt blk03399.txt blk03400.txt blk03401.txt blk03402.txt blk03403.txt blk03404.txt blk03405.txt blk03406.txt blk03407.txt blk03408.txt blk03409.txt blk03410.txt blk03411.txt blk03412.txt blk03413.txt blk03414.txt blk03415.txt blk03416.txt blk03417.txt blk03418.txt blk03419.txt blk03420.txt blk03421.txt blk03422.txt blk03423.txt blk03424.txt blk03425.txt blk03426.txt blk03427.txt blk03428.txt blk03429.txt blk03430.txt blk03431.txt blk03432.txt blk03433.txt blk03434.txt blk03435.txt blk03436.txt blk03437.txt blk03438.txt blk03439.txt blk03440.txt blk03441.txt blk03442.txt blk03443.txt blk03444.txt blk03445.txt blk03446.txt blk03447.txt blk03448.txt blk03449.txt blk03450.txt blk03451.txt blk03452.txt blk03453.txt blk03454.txt blk03455.txt blk03456.txt blk03457.txt blk03458.txt blk03459.txt blk03460.txt blk03461.txt blk03462.txt blk03463.txt blk03464.txt blk03465.txt blk03466.txt blk03467.txt blk03468.txt blk03469.txt blk03470.txt blk03471.txt blk03472.txt blk03473.txt blk03474.txt blk03475.txt blk03476.txt blk03477.txt blk03478.txt blk03479.txt blk03480.txt blk03481.txt blk03482.txt blk03483.txt blk03484.txt blk03485.txt blk03486.txt blk03487.txt blk03488.txt blk03489.txt blk03490.txt blk03491.txt blk03492.txt blk03493.txt blk03494.txt blk03495.txt blk03496.txt blk03497.txt blk03498.txt blk03499.txt blk03500.txt blk03501.txt blk03502.txt blk03503.txt blk03504.txt blk03505.txt blk03506.txt blk03507.txt blk03508.txt blk03509.txt blk03510.txt blk03511.txt blk03512.txt blk03513.txt blk03514.txt blk03515.txt blk03516.txt blk03517.txt blk03518.txt blk03519.txt blk03520.txt blk03521.txt blk03522.txt blk03523.txt blk03524.txt blk03525.txt blk03526.txt blk03527.txt blk03528.txt blk03529.txt blk03530.txt blk03531.txt blk03532.txt blk03533.txt blk03534.txt blk03535.txt blk03536.txt blk03537.txt blk03538.txt blk03539.txt blk03540.txt blk03541.txt blk03542.txt blk03543.txt blk03544.txt blk03545.txt blk03546.txt blk03547.txt blk03548.txt blk03549.txt blk03550.txt blk03551.txt blk03552.txt blk03553.txt blk03554.txt blk03555.txt blk03556.txt blk03557.txt blk03558.txt blk03559.txt blk03560.txt blk03561.txt blk03562.txt blk03563.txt blk03564.txt blk03565.txt blk03566.txt blk03567.txt blk03568.txt blk03569.txt blk03570.txt blk03571.txt blk03572.txt blk03573.txt blk03574.txt blk03575.txt blk03576.txt blk03577.txt blk03578.txt blk03579.txt blk03580.txt blk03581.txt blk03582.txt blk03583.txt blk03584.txt blk03585.txt blk03586.txt blk03587.txt blk03588.txt blk03589.txt blk03590.txt blk03591.txt blk03592.txt blk03593.txt blk03594.txt blk03595.txt blk03596.txt blk03597.txt blk03598.txt blk03599.txt blk03600.txt blk03601.txt blk03602.txt blk03603.txt blk03604.txt blk03605.txt blk03606.txt blk03607.txt blk03608.txt blk03609.txt blk03610.txt blk03611.txt blk03612.txt blk03613.txt blk03614.txt blk03615.txt blk03616.txt blk03617.txt blk03618.txt blk03619.txt blk03620.txt blk03621.txt blk03622.txt blk03623.txt blk03624.txt blk03625.txt blk03626.txt blk03627.txt blk03628.txt blk03629.txt blk03630.txt blk03631.txt blk03632.txt blk03633.txt blk03634.txt blk03635.txt blk03636.txt blk03637.txt blk03638.txt blk03639.txt blk03640.txt blk03641.txt blk03642.txt blk03643.txt blk03644.txt blk03645.txt blk03646.txt blk03647.txt blk03648.txt blk03649.txt blk03650.txt blk03651.txt blk03652.txt blk03653.txt blk03654.txt blk03655.txt blk03656.txt blk03657.txt blk03658.txt blk03659.txt blk03660.txt blk03661.txt blk03662.txt blk03663.txt blk03664.txt blk03665.txt blk03666.txt blk03667.txt blk03668.txt blk03669.txt blk03670.txt blk03671.txt blk03672.txt blk03673.txt blk03674.txt blk03675.txt blk03676.txt blk03677.txt blk03678.txt blk03679.txt blk03680.txt blk03681.txt blk03682.txt blk03683.txt blk03684.txt blk03685.txt blk03686.txt blk03687.txt blk03688.txt blk03689.txt blk03690.txt blk03691.txt blk03692.txt blk03693.txt blk03694.txt blk03695.txt blk03696.txt blk03697.txt blk03698.txt blk03699.txt blk03700.txt blk03701.txt blk03702.txt blk03703.txt blk03704.txt blk03705.txt blk03706.txt blk03707.txt blk03708.txt blk03709.txt blk03710.txt blk03711.txt blk03712.txt blk03713.txt blk03714.txt blk03715.txt blk03716.txt blk03717.txt blk03718.txt blk03719.txt blk03720.txt blk03721.txt blk03722.txt blk03723.txt blk03724.txt blk03725.txt blk03726.txt blk03727.txt blk03728.txt blk03729.txt blk03730.txt blk03731.txt blk03732.txt blk03733.txt blk03734.txt blk03735.txt blk03736.txt blk03737.txt blk03738.txt blk03739.txt blk03740.txt blk03741.txt blk03742.txt blk03743.txt blk03744.txt blk03745.txt blk03746.txt blk03747.txt blk03748.txt blk03749.txt blk03750.txt blk03751.txt blk03752.txt blk03753.txt blk03754.txt blk03755.txt blk03756.txt blk03757.txt blk03758.txt blk03759.txt blk03760.txt blk03761.txt blk03762.txt blk03763.txt blk03764.txt blk03765.txt blk03766.txt blk03767.txt blk03768.txt blk03769.txt blk03770.txt blk03771.txt blk03772.txt blk03773.txt blk03774.txt blk03775.txt blk03776.txt blk03777.txt blk03778.txt blk03779.txt blk03780.txt blk03781.txt blk03782.txt blk03783.txt blk03784.txt blk03785.txt blk03786.txt blk03787.txt blk03788.txt blk03789.txt blk03790.txt blk03791.txt blk03792.txt blk03793.txt blk03794.txt blk03795.txt blk03796.txt blk03797.txt blk03798.txt blk03799.txt blk03800.txt blk03801.txt blk03802.txt blk03803.txt blk03804.txt blk03805.txt blk03806.txt blk03807.txt blk03808.txt blk03809.txt blk03810.txt blk03811.txt blk03812.txt blk03813.txt blk03814.txt blk03815.txt blk03816.txt blk03817.txt blk03818.txt blk03819.txt blk03820.txt blk03821.txt blk03822.txt blk03823.txt blk03824.txt blk03825.txt blk03826.txt blk03827.txt blk03828.txt blk03829.txt blk03830.txt blk03831.txt blk03832.txt blk03833.txt blk03834.txt blk03835.txt blk03836.txt blk03837.txt blk03838.txt blk03839.txt blk03840.txt blk03841.txt blk03842.txt blk03843.txt blk03844.txt blk03845.txt blk03846.txt blk03847.txt blk03848.txt blk03849.txt blk03850.txt blk03851.txt blk03852.txt blk03853.txt blk03854.txt blk03855.txt blk03856.txt blk03857.txt blk03858.txt blk03859.txt blk03860.txt blk03861.txt blk03862.txt blk03863.txt blk03864.txt blk03865.txt blk03866.txt blk03867.txt blk03868.txt blk03869.txt blk03870.txt blk03871.txt blk03872.txt blk03873.txt blk03874.txt blk03875.txt blk03876.txt blk03877.txt blk03878.txt blk03879.txt blk03880.txt blk03881.txt blk03882.txt blk03883.txt blk03884.txt blk03885.txt blk03886.txt blk03887.txt blk03888.txt blk03889.txt blk03890.txt blk03891.txt blk03892.txt blk03893.txt blk03894.txt blk03895.txt blk03896.txt blk03897.txt blk03898.txt blk03899.txt blk03900.txt blk03901.txt blk03902.txt blk03903.txt blk03904.txt blk03905.txt blk03906.txt blk03907.txt blk03908.txt blk03909.txt blk03910.txt blk03911.txt blk03912.txt blk03913.txt blk03914.txt blk03915.txt blk03916.txt blk03917.txt blk03918.txt blk03919.txt blk03920.txt blk03921.txt blk03922.txt blk03923.txt blk03924.txt blk03925.txt blk03926.txt blk03927.txt blk03928.txt blk03929.txt blk03930.txt blk03931.txt blk03932.txt blk03933.txt blk03934.txt blk03935.txt blk03936.txt blk03937.txt blk03938.txt blk03939.txt blk03940.txt blk03941.txt blk03942.txt blk03943.txt blk03944.txt blk03945.txt blk03946.txt blk03947.txt blk03948.txt blk03949.txt blk03950.txt blk03951.txt blk03952.txt blk03953.txt blk03954.txt blk03955.txt blk03956.txt blk03957.txt blk03958.txt blk03959.txt blk03960.txt blk03961.txt blk03962.txt blk03963.txt blk03964.txt blk03965.txt blk03966.txt blk03967.txt blk03968.txt blk03969.txt blk03970.txt blk03971.txt blk03972.txt blk03973.txt blk03974.txt blk03975.txt blk03976.txt blk03977.txt blk03978.txt blk03979.txt blk03980.txt blk03981.txt blk03982.txt blk03983.txt blk03984.txt blk03985.txt blk03986.txt blk03987.txt blk03988.txt blk03989.txt blk03990.txt blk03991.txt blk03992.txt blk03993.txt blk03994.txt blk03995.txt blk03996.txt blk03997.txt blk03998.txt blk03999.txt blk04000.txt blk04001.txt blk04002.txt blk04003.txt blk04004.txt blk04005.txt blk04006.txt blk04007.txt blk04008.txt blk04009.txt blk04010.txt blk04011.txt blk04012.txt blk04013.txt blk04014.txt blk04015.txt blk04016.txt blk04017.txt blk04018.txt blk04019.txt blk04020.txt blk04021.txt blk04022.txt blk04023.txt blk04024.txt blk04025.txt blk04026.txt blk04027.txt blk04028.txt blk04029.txt blk04030.txt blk04031.txt blk04032.txt blk04033.txt blk04034.txt blk04035.txt blk04036.txt blk04037.txt blk04038.txt blk04039.txt blk04040.txt blk04041.txt blk04042.txt blk04043.txt blk04044.txt blk04045.txt blk04046.txt blk04047.txt blk04048.txt blk04049.txt blk04050.txt blk04051.txt blk04052.txt blk04053.txt blk04054.txt blk04055.txt blk04056.txt blk04057.txt blk04058.txt blk04059.txt blk04060.txt blk04061.txt blk04062.txt blk04063.txt blk04064.txt blk04065.txt blk04066.txt blk04067.txt blk04068.txt blk04069.txt blk04070.txt blk04071.txt blk04072.txt blk04073.txt blk04074.txt blk04075.txt blk04076.txt blk04077.txt blk04078.txt blk04079.txt blk04080.txt blk04081.txt blk04082.txt blk04083.txt blk04084.txt blk04085.txt blk04086.txt blk04087.txt blk04088.txt blk04089.txt blk04090.txt blk04091.txt blk04092.txt blk04093.txt blk04094.txt blk04095.txt blk04096.txt blk04097.txt blk04098.txt blk04099.txt blk04100.txt blk04101.txt blk04102.txt blk04103.txt blk04104.txt blk04105.txt blk04106.txt blk04107.txt blk04108.txt blk04109.txt blk04110.txt blk04111.txt blk04112.txt blk04113.txt blk04114.txt blk04115.txt blk04116.txt blk04117.txt blk04118.txt blk04119.txt blk04120.txt blk04121.txt blk04122.txt blk04123.txt blk04124.txt blk04125.txt blk04126.txt blk04127.txt blk04128.txt blk04129.txt blk04130.txt blk04131.txt blk04132.txt blk04133.txt blk04134.txt blk04135.txt blk04136.txt blk04137.txt blk04138.txt blk04139.txt blk04140.txt blk04141.txt blk04142.txt blk04143.txt blk04144.txt blk04145.txt blk04146.txt blk04147.txt blk04148.txt blk04149.txt blk04150.txt blk04151.txt blk04152.txt blk04153.txt blk04154.txt blk04155.txt blk04156.txt blk04157.txt blk04158.txt blk04159.txt blk04160.txt blk04161.txt blk04162.txt blk04163.txt blk04164.txt blk04165.txt blk04166.txt blk04167.txt blk04168.txt blk04169.txt blk04170.txt blk04171.txt blk04172.txt blk04173.txt blk04174.txt blk04175.txt blk04176.txt blk04177.txt blk04178.txt blk04179.txt blk04180.txt blk04181.txt blk04182.txt blk04183.txt blk04184.txt blk04185.txt blk04186.txt blk04187.txt blk04188.txt blk04189.txt blk04190.txt blk04191.txt blk04192.txt blk04193.txt blk04194.txt blk04195.txt blk04196.txt blk04197.txt blk04198.txt blk04199.txt blk04200.txt blk04201.txt blk04202.txt blk04203.txt blk04204.txt blk04205.txt blk04206.txt blk04207.txt blk04208.txt blk04209.txt blk04210.txt blk04211.txt blk04212.txt blk04213.txt blk04214.txt blk04215.txt blk04216.txt blk04217.txt blk04218.txt blk04219.txt blk04220.txt blk04221.txt blk04222.txt blk04223.txt blk04224.txt blk04225.txt blk04226.txt blk04227.txt blk04228.txt blk04229.txt blk04230.txt blk04231.txt blk04232.txt blk04233.txt blk04234.txt blk04235.txt blk04236.txt blk04237.txt blk04238.txt blk04239.txt blk04240.txt blk04241.txt blk04242.txt blk04243.txt blk04244.txt blk04245.txt blk04246.txt blk04247.txt blk04248.txt blk04249.txt blk04250.txt blk04251.txt blk04252.txt blk04253.txt blk04254.txt blk04255.txt blk04256.txt blk04257.txt blk04258.txt blk04259.txt blk04260.txt blk04261.txt blk04262.txt blk04263.txt blk04264.txt blk04265.txt blk04266.txt blk04267.txt blk04268.txt blk04269.txt blk04270.txt blk04271.txt blk04272.txt blk04273.txt blk04274.txt blk04275.txt blk04276.txt blk04277.txt blk04278.txt blk04279.txt blk04280.txt blk04281.txt blk04282.txt blk04283.txt blk04284.txt blk04285.txt blk04286.txt blk04287.txt blk04288.txt blk04289.txt blk04290.txt blk04291.txt blk04292.txt blk04293.txt blk04294.txt blk04295.txt blk04296.txt blk04297.txt blk04298.txt blk04299.txt blk04300.txt blk04301.txt blk04302.txt blk04303.txt blk04304.txt blk04305.txt blk04306.txt blk04307.txt blk04308.txt blk04309.txt blk04310.txt blk04311.txt blk04312.txt blk04313.txt blk04314.txt blk04315.txt blk04316.txt blk04317.txt blk04318.txt blk04319.txt blk04320.txt blk04321.txt blk04322.txt blk04323.txt blk04324.txt blk04325.txt blk04326.txt blk04327.txt blk04328.txt blk04329.txt blk04330.txt blk04331.txt blk04332.txt blk04333.txt blk04334.txt blk04335.txt blk04336.txt blk04337.txt blk04338.txt blk04339.txt blk04340.txt blk04341.txt blk04342.txt blk04343.txt blk04344.txt blk04345.txt blk04346.txt blk04347.txt blk04348.txt blk04349.txt blk04350.txt blk04351.txt blk04352.txt blk04353.txt blk04354.txt blk04355.txt blk04356.txt blk04357.txt blk04358.txt blk04359.txt blk04360.txt blk04361.txt blk04362.txt blk04363.txt blk04364.txt blk04365.txt blk04366.txt blk04367.txt blk04368.txt blk04369.txt blk04370.txt blk04371.txt blk04372.txt blk04373.txt blk04374.txt blk04375.txt blk04376.txt blk04377.txt blk04378.txt blk04379.txt blk04380.txt blk04381.txt blk04382.txt blk04383.txt blk04384.txt blk04385.txt blk04386.txt blk04387.txt blk04388.txt blk04389.txt blk04390.txt blk04391.txt blk04392.txt blk04393.txt blk04394.txt blk04395.txt blk04396.txt blk04397.txt blk04398.txt blk04399.txt blk04400.txt blk04401.txt blk04402.txt blk04403.txt blk04404.txt blk04405.txt blk04406.txt blk04407.txt blk04408.txt blk04409.txt blk04410.txt blk04411.txt blk04412.txt blk04413.txt blk04414.txt blk04415.txt blk04416.txt blk04417.txt blk04418.txt blk04419.txt blk04420.txt blk04421.txt blk04422.txt blk04423.txt blk04424.txt blk04425.txt blk04426.txt blk04427.txt blk04428.txt blk04429.txt blk04430.txt blk04431.txt blk04432.txt blk04433.txt blk04434.txt blk04435.txt blk04436.txt blk04437.txt blk04438.txt blk04439.txt blk04440.txt blk04441.txt blk04442.txt blk04443.txt blk04444.txt blk04445.txt blk04446.txt blk04447.txt blk04448.txt blk04449.txt blk04450.txt blk04451.txt blk04452.txt blk04453.txt blk04454.txt blk04455.txt blk04456.txt blk04457.txt blk04458.txt blk04459.txt blk04460.txt blk04461.txt blk04462.txt blk04463.txt blk04464.txt blk04465.txt blk04466.txt blk04467.txt blk04468.txt blk04469.txt blk04470.txt blk04471.txt blk04472.txt blk04473.txt blk04474.txt blk04475.txt blk04476.txt blk04477.txt blk04478.txt blk04479.txt blk04480.txt blk04481.txt blk04482.txt blk04483.txt blk04484.txt blk04485.txt blk04486.txt blk04487.txt blk04488.txt blk04489.txt blk04490.txt blk04491.txt blk04492.txt blk04493.txt blk04494.txt blk04495.txt blk04496.txt blk04497.txt blk04498.txt blk04499.txt blk04500.txt blk04501.txt blk04502.txt blk04503.txt blk04504.txt blk04505.txt blk04506.txt blk04507.txt blk04508.txt blk04509.txt blk04510.txt blk04511.txt blk04512.txt blk04513.txt blk04514.txt blk04515.txt blk04516.txt blk04517.txt blk04518.txt blk04519.txt blk04520.txt blk04521.txt blk04522.txt blk04523.txt blk04524.txt blk04525.txt blk04526.txt blk04527.txt blk04528.txt blk04529.txt blk04530.txt blk04531.txt blk04532.txt blk04533.txt blk04534.txt blk04535.txt blk04536.txt blk04537.txt blk04538.txt blk04539.txt blk04540.txt blk04541.txt blk04542.txt blk04543.txt blk04544.txt blk04545.txt blk04546.txt blk04547.txt blk04548.txt blk04549.txt blk04550.txt blk04551.txt blk04552.txt blk04553.txt blk04554.txt blk04555.txt blk04556.txt blk04557.txt blk04558.txt blk04559.txt blk04560.txt blk04561.txt blk04562.txt blk04563.txt blk04564.txt blk04565.txt blk04566.txt blk04567.txt blk04568.txt blk04569.txt blk04570.txt blk04571.txt blk04572.txt blk04573.txt blk04574.txt blk04575.txt blk04576.txt blk04577.txt blk04578.txt blk04579.txt blk04580.txt blk04581.txt blk04582.txt blk04583.txt blk04584.txt blk04585.txt blk04586.txt blk04587.txt blk04588.txt blk04589.txt blk04590.txt blk04591.txt blk04592.txt blk04593.txt blk04594.txt blk04595.txt blk04596.txt blk04597.txt blk04598.txt blk04599.txt blk04600.txt blk04601.txt blk04602.txt blk04603.txt blk04604.txt blk04605.txt blk04606.txt blk04607.txt blk04608.txt blk04609.txt blk04610.txt blk04611.txt blk04612.txt blk04613.txt blk04614.txt blk04615.txt blk04616.txt blk04617.txt blk04618.txt blk04619.txt blk04620.txt blk04621.txt blk04622.txt blk04623.txt blk04624.txt blk04625.txt blk04626.txt blk04627.txt blk04628.txt blk04629.txt blk04630.txt blk04631.txt blk04632.txt blk04633.txt blk04634.txt blk04635.txt blk04636.txt blk04637.txt blk04638.txt blk04639.txt blk04640.txt blk04641.txt blk04642.txt blk04643.txt blk04644.txt blk04645.txt blk04646.txt blk04647.txt blk04648.txt blk04649.txt blk04650.txt blk04651.txt blk04652.txt blk04653.txt blk04654.txt blk04655.txt blk04656.txt blk04657.txt blk04658.txt blk04659.txt blk04660.txt blk04661.txt blk04662.txt blk04663.txt blk04664.txt blk04665.txt blk04666.txt blk04667.txt blk04668.txt blk04669.txt blk04670.txt blk04671.txt blk04672.txt blk04673.txt blk04674.txt blk04675.txt blk04676.txt blk04677.txt blk04678.txt blk04679.txt blk04680.txt blk04681.txt blk04682.txt blk04683.txt blk04684.txt blk04685.txt blk04686.txt blk04687.txt blk04688.txt blk04689.txt blk04690.txt blk04691.txt blk04692.txt blk04693.txt blk04694.txt blk04695.txt blk04696.txt blk04697.txt blk04698.txt blk04699.txt blk04700.txt blk04701.txt blk04702.txt blk04703.txt blk04704.txt blk04705.txt blk04706.txt blk04707.txt blk04708.txt blk04709.txt blk04710.txt blk04711.txt blk04712.txt blk04713.txt blk04714.txt blk04715.txt blk04716.txt blk04717.txt blk04718.txt blk04719.txt blk04720.txt blk04721.txt blk04722.txt blk04723.txt blk04724.txt blk04725.txt blk04726.txt blk04727.txt blk04728.txt blk04729.txt blk04730.txt blk04731.txt blk04732.txt blk04733.txt blk04734.txt blk04735.txt blk04736.txt blk04737.txt blk04738.txt blk04739.txt blk04740.txt blk04741.txt blk04742.txt blk04743.txt blk04744.txt blk04745.txt blk04746.txt blk04747.txt blk04748.txt blk04749.txt blk04750.txt blk04751.txt blk04752.txt blk04753.txt blk04754.txt blk04755.txt blk04756.txt blk04757.txt blk04758.txt blk04759.txt blk04760.txt blk04761.txt blk04762.txt blk04763.txt blk04764.txt blk04765.txt blk04766.txt blk04767.txt blk04768.txt blk04769.txt blk04770.txt blk04771.txt blk04772.txt blk04773.txt blk04774.txt blk04775.txt blk04776.txt blk04777.txt blk04778.txt blk04779.txt blk04780.txt blk04781.txt blk04782.txt blk04783.txt blk04784.txt blk04785.txt blk04786.txt blk04787.txt blk04788.txt blk04789.txt blk04790.txt blk04791.txt blk04792.txt blk04793.txt blk04794.txt blk04795.txt blk04796.txt blk04797.txt blk04798.txt blk04799.txt blk04800.txt blk04801.txt blk04802.txt blk04803.txt blk04804.txt blk04805.txt blk04806.txt blk04807.txt blk04808.txt blk04809.txt blk04810.txt blk04811.txt blk04812.txt blk04813.txt blk04814.txt blk04815.txt blk04816.txt blk04817.txt blk04818.txt blk04819.txt blk04820.txt blk04821.txt blk04822.txt blk04823.txt blk04824.txt blk04825.txt blk04826.txt blk04827.txt blk04828.txt blk04829.txt blk04830.txt blk04831.txt blk04832.txt blk04833.txt blk04834.txt blk04835.txt blk04836.txt blk04837.txt blk04838.txt blk04839.txt blk04840.txt blk04841.txt blk04842.txt blk04843.txt blk04844.txt blk04845.txt blk04846.txt blk04847.txt blk04848.txt blk04849.txt blk04850.txt blk04851.txt blk04852.txt blk04853.txt blk04854.txt blk04855.txt blk04856.txt blk04857.txt blk04858.txt blk04859.txt blk04860.txt blk04861.txt blk04862.txt blk04863.txt blk04864.txt blk04865.txt blk04866.txt blk04867.txt blk04868.txt blk04869.txt blk04870.txt blk04871.txt blk04872.txt blk04873.txt blk04874.txt blk04875.txt blk04876.txt blk04877.txt blk04878.txt blk04879.txt blk04880.txt blk04881.txt blk04882.txt blk04883.txt blk04884.txt blk04885.txt blk04886.txt blk04887.txt blk04888.txt blk04889.txt blk04890.txt blk04891.txt Show all files
Advertisement: